diff options
author | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
commit | 628590d7ec80e10a3fc24b1c18a1afb55cca10a8 (patch) | |
tree | 4b1c3f52d86d7fb53afbe9e9438468588fa489f8 /services/robotests | |
parent | b11b8ec3aec8bb42f2c07e1c5ac7942da293baa8 (diff) | |
parent | d2d3a20624d968199353ccf6ddbae6f3ac39c9af (diff) |
Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27
Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
Diffstat (limited to 'services/robotests')
53 files changed, 1938 insertions, 3590 deletions
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp index 566e61e1a14f..1ae2aec90ba3 100644 --- a/services/robotests/Android.bp +++ b/services/robotests/Android.bp @@ -46,6 +46,9 @@ android_robolectric_test { "services.backup", "testng", ], + static_libs: [ + "androidx.test.ext.truth", + ], instrumentation_for: "FrameworksServicesLib", } diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp index a3ccc6e9dd12..32587accf160 100644 --- a/services/robotests/backup/Android.bp +++ b/services/robotests/backup/Android.bp @@ -46,8 +46,10 @@ android_robolectric_test { // Include the testing libraries libs: [ + "mockito-robolectric-prebuilt", "platform-test-annotations", "testng", + "truth-prebuilt", ], instrumentation_for: "BackupFrameworksServicesLib", diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java index 0cb21d014054..a1bfcdf4bdfa 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,14 +21,17 @@ import static android.Manifest.permission.DUMP; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.PACKAGE_USAGE_STATS; -import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread; import static com.android.server.backup.testing.TransportData.backupTransport; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.robolectric.Shadows.shadowOf; import static org.testng.Assert.expectThrows; @@ -43,15 +46,19 @@ import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.UserHandle; +import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.util.SparseArray; import com.android.server.backup.testing.TransportData; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowBinder; +import com.android.server.testing.shadows.ShadowEnvironment; +import com.android.server.testing.shadows.ShadowSystemServiceRegistry; +import com.android.server.testing.shadows.ShadowUserManager; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,6 +67,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowContextWrapper; import java.io.File; @@ -68,23 +76,32 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */ +/** Tests for {@link BackupManagerService}. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowApplicationPackageManager.class, ShadowBinder.class}) +@Config( + shadows = { + ShadowApplicationPackageManager.class, + ShadowBinder.class, + ShadowUserManager.class, + ShadowEnvironment.class, + ShadowSystemServiceRegistry.class + }) @Presubmit -public class BackupManagerServiceTest { +public class BackupManagerServiceRoboTest { private static final String TEST_PACKAGE = "package"; private static final String TEST_TRANSPORT = "transport"; private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE}; - private ShadowContextWrapper mShadowContext; private Context mContext; + private ShadowContextWrapper mShadowContext; + private ShadowUserManager mShadowUserManager; @UserIdInt private int mUserOneId; @UserIdInt private int mUserTwoId; + @Mock private UserBackupManagerService mUserSystemService; @Mock private UserBackupManagerService mUserOneService; @Mock private UserBackupManagerService mUserTwoService; - /** Initialize {@link BackupManagerService}. */ + /** Setup */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -92,79 +109,28 @@ public class BackupManagerServiceTest { Application application = RuntimeEnvironment.application; mContext = application; mShadowContext = shadowOf(application); + mShadowUserManager = Shadow.extract(UserManager.get(application)); mUserOneId = UserHandle.USER_SYSTEM + 1; mUserTwoId = mUserOneId + 1; - } - - /** - * Clean up and reset state that was created for testing {@link BackupManagerService} - * operations. - */ - @After - public void tearDown() throws Exception { - ShadowBinder.reset(); - } + mShadowUserManager.addUser(mUserOneId, "mUserOneId", 0); + mShadowUserManager.addUser(mUserTwoId, "mUserTwoId", 0); - /** - * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is - * specifically to prevent overloading the logs in production. - */ - @Test - public void testMoreDebug_isFalse() throws Exception { - boolean moreDebug = BackupManagerService.MORE_DEBUG; - - assertThat(moreDebug).isFalse(); - } - - /** Test that the constructor does not create {@link UserBackupManagerService} instances. */ - @Test - public void testConstructor_doesNotRegisterUsers() throws Exception { - BackupManagerService backupManagerService = createService(); - - assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0); - } - - /** Test that the constructor handles {@code null} parameters. */ - @Test - public void testConstructor_withNullContext_throws() throws Exception { - expectThrows( - NullPointerException.class, - () -> - new BackupManagerService( - /* context */ null, - new Trampoline(mContext), - startBackupThread(null))); - } - - /** Test that the constructor handles {@code null} parameters. */ - @Test - public void testConstructor_withNullTrampoline_throws() throws Exception { - expectThrows( - NullPointerException.class, - () -> - new BackupManagerService( - mContext, /* trampoline */ null, startBackupThread(null))); - } + mShadowContext.grantPermissions(BACKUP); + mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL); - /** Test that the constructor handles {@code null} parameters. */ - @Test - public void testConstructor_withNullBackupThread_throws() throws Exception { - expectThrows( - NullPointerException.class, - () -> - new BackupManagerService( - mContext, new Trampoline(mContext), /* backupThread */ null)); + ShadowBinder.setCallingUid(Process.SYSTEM_UID); } /** Test that the service registers users. */ @Test public void testStartServiceForUser_registersUser() throws Exception { BackupManagerService backupManagerService = createService(); + backupManagerService.setBackupServiceActive(mUserOneId, true); backupManagerService.startServiceForUser(mUserOneId); - SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices(); assertThat(serviceUsers.size()).isEqualTo(1); assertThat(serviceUsers.get(mUserOneId)).isNotNull(); } @@ -173,10 +139,11 @@ public class BackupManagerServiceTest { @Test public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception { BackupManagerService backupManagerService = createService(); + backupManagerService.setBackupServiceActive(mUserOneId, true); backupManagerService.startServiceForUser(mUserOneId, mUserOneService); - SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices(); assertThat(serviceUsers.size()).isEqualTo(1); assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService); } @@ -187,10 +154,11 @@ public class BackupManagerServiceTest { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + ShadowBinder.setCallingUid(Process.SYSTEM_UID); backupManagerService.stopServiceForUser(mUserOneId); - SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices(); assertThat(serviceUsers.size()).isEqualTo(1); assertThat(serviceUsers.get(mUserOneId)).isNull(); assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService); @@ -201,6 +169,7 @@ public class BackupManagerServiceTest { public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + backupManagerService.setBackupServiceActive(mUserTwoId, true); backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); backupManagerService.stopServiceForUser(mUserOneId); @@ -213,60 +182,15 @@ public class BackupManagerServiceTest { @Test public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception { BackupManagerService backupManagerService = createService(); + backupManagerService.setBackupServiceActive(mUserOneId, true); + ShadowBinder.setCallingUid(Process.SYSTEM_UID); backupManagerService.stopServiceForUser(mUserOneId); - SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers(); + SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices(); assertThat(serviceUsers.size()).isEqualTo(0); } - /** - * Test that the backup services throws a {@link SecurityException} if the caller does not have - * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. - */ - @Test - public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - - expectThrows( - SecurityException.class, - () -> - backupManagerService.getServiceForUserIfCallerHasPermission( - mUserOneId, "test")); - } - - /** - * Test that the backup services does not throw a {@link SecurityException} if the caller has - * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. - */ - @Test - public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true); - - assertEquals( - mUserOneService, - backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test")); - } - - /** - * Test that the backup services does not throw a {@link SecurityException} if the caller does - * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id. - */ - @Test - public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - - assertEquals( - mUserOneService, - backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test")); - } - // --------------------------------------------- // Backup agent tests // --------------------------------------------- @@ -274,8 +198,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE); @@ -286,8 +210,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE); @@ -298,8 +222,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); IBinder agentBinder = mock(IBinder.class); @@ -311,8 +235,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); IBinder agentBinder = mock(IBinder.class); @@ -323,33 +247,9 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test - public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - - backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE); - - verify(mUserOneService).agentDisconnected(TEST_PACKAGE); - } - - /** Test that the backup service does not route methods for non-registered users. */ - @Test - public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - - backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE); - - verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L); @@ -360,8 +260,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L); @@ -376,8 +276,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); String[] transports = {TEST_TRANSPORT}; @@ -389,8 +289,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); String[] transports = {TEST_TRANSPORT}; @@ -402,8 +302,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE); @@ -414,8 +314,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE); @@ -426,8 +326,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.getCurrentTransport(mUserOneId); @@ -438,8 +338,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.getCurrentTransport(mUserTwoId); @@ -451,8 +351,8 @@ public class BackupManagerServiceTest { @Test public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.getCurrentTransportComponent(mUserOneId); @@ -464,8 +364,8 @@ public class BackupManagerServiceTest { @Test public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.getCurrentTransportComponent(mUserTwoId); @@ -476,8 +376,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.listAllTransports(mUserOneId); @@ -488,8 +388,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.listAllTransports(mUserTwoId); @@ -501,8 +401,8 @@ public class BackupManagerServiceTest { @Test public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.listAllTransportComponents(mUserOneId); @@ -514,8 +414,8 @@ public class BackupManagerServiceTest { @Test public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.listAllTransportComponents(mUserTwoId); @@ -525,69 +425,9 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test - public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser() - throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - TransportData transport = backupTransport(); - Intent configurationIntent = new Intent(); - Intent dataManagementIntent = new Intent(); - - backupManagerService.updateTransportAttributes( - mUserOneId, - transport.getTransportComponent(), - transport.transportName, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - - verify(mUserOneService) - .updateTransportAttributes( - transport.getTransportComponent(), - transport.transportName, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - } - - /** Test that the backup service does not route methods for non-registered users. */ - @Test - public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall() - throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - TransportData transport = backupTransport(); - Intent configurationIntent = new Intent(); - Intent dataManagementIntent = new Intent(); - - backupManagerService.updateTransportAttributes( - mUserTwoId, - transport.getTransportComponent(), - transport.transportName, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - - verify(mUserOneService, never()) - .updateTransportAttributes( - transport.getTransportComponent(), - transport.transportName, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT); @@ -598,8 +438,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT); @@ -610,8 +450,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); TransportData transport = backupTransport(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); @@ -627,8 +467,8 @@ public class BackupManagerServiceTest { @Test public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); TransportData transport = backupTransport(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); @@ -643,8 +483,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT); @@ -655,8 +495,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT); @@ -667,8 +507,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT); @@ -679,8 +519,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT); @@ -691,8 +531,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT); @@ -703,8 +543,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT); @@ -715,8 +555,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT); @@ -727,8 +567,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT); @@ -736,17 +576,78 @@ public class BackupManagerServiceTest { verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT); } + /** Test that the backup service routes methods correctly to the user that requests it. */ + @Test + public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser() + throws Exception { + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + TransportData transport = backupTransport(); + Intent configurationIntent = new Intent(); + Intent dataManagementIntent = new Intent(); + + backupManagerService.updateTransportAttributes( + mUserOneId, + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + + verify(mUserOneService) + .updateTransportAttributes( + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + } + + /** Test that the backup service does not route methods for non-registered users. */ + @Test + public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall() + throws Exception { + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + TransportData transport = backupTransport(); + Intent configurationIntent = new Intent(); + Intent dataManagementIntent = new Intent(); + + backupManagerService.updateTransportAttributes( + mUserTwoId, + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + + verify(mUserOneService, never()) + .updateTransportAttributes( + transport.getTransportComponent(), + transport.transportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + } + // --------------------------------------------- // Settings tests // --------------------------------------------- + /** * Test that the backup services throws a {@link SecurityException} if the caller does not have * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. */ @Test public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); expectThrows( @@ -760,9 +661,10 @@ public class BackupManagerServiceTest { */ @Test public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); backupManagerService.setBackupEnabled(mUserTwoId, true); @@ -773,8 +675,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.setBackupEnabled(mUserOneId, true); @@ -785,8 +687,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.setBackupEnabled(mUserTwoId, true); @@ -797,8 +699,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.setAutoRestore(mUserOneId, true); @@ -809,8 +711,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.setAutoRestore(mUserTwoId, true); @@ -821,8 +723,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.isBackupEnabled(mUserOneId); @@ -833,8 +735,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.isBackupEnabled(mUserTwoId); @@ -849,8 +751,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE); @@ -861,8 +763,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE); @@ -874,8 +776,8 @@ public class BackupManagerServiceTest { @Test public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; @@ -888,8 +790,8 @@ public class BackupManagerServiceTest { @Test public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; @@ -904,8 +806,8 @@ public class BackupManagerServiceTest { */ @Test public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId)); @@ -917,9 +819,10 @@ public class BackupManagerServiceTest { */ @Test public void testBackupNow_withPermission_propagatesForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); backupManagerService.backupNow(mUserTwoId); @@ -930,8 +833,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.backupNow(mUserOneId); @@ -942,8 +845,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.backupNow(mUserTwoId); @@ -957,8 +860,8 @@ public class BackupManagerServiceTest { */ @Test public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; IBackupObserver observer = mock(IBackupObserver.class); @@ -977,9 +880,10 @@ public class BackupManagerServiceTest { */ @Test public void testRequestBackup_withPermission_propagatesForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); + String[] packages = {TEST_PACKAGE}; IBackupObserver observer = mock(IBackupObserver.class); IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); @@ -993,8 +897,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); String[] packages = {TEST_PACKAGE}; IBackupObserver observer = mock(IBackupObserver.class); IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); @@ -1008,8 +912,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); String[] packages = {TEST_PACKAGE}; IBackupObserver observer = mock(IBackupObserver.class); IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); @@ -1026,8 +930,8 @@ public class BackupManagerServiceTest { */ @Test public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId)); @@ -1039,9 +943,9 @@ public class BackupManagerServiceTest { */ @Test public void testCancelBackups_withPermission_propagatesForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); backupManagerService.cancelBackups(mUserTwoId); @@ -1052,8 +956,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.cancelBackups(mUserOneId); @@ -1064,8 +968,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.cancelBackups(mUserTwoId); @@ -1076,8 +980,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService); FullBackupJob job = new FullBackupJob(); backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job); @@ -1099,8 +1003,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService); backupManagerService.endFullBackup(UserHandle.USER_SYSTEM); @@ -1120,8 +1024,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; @@ -1133,8 +1037,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; @@ -1150,8 +1054,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0); @@ -1162,8 +1066,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0); @@ -1174,8 +1078,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT); @@ -1186,8 +1090,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT); @@ -1199,8 +1103,8 @@ public class BackupManagerServiceTest { @Test public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE); @@ -1211,8 +1115,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE); @@ -1227,8 +1131,9 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService); + ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM)); backupManagerService.setBackupPassword("currentPassword", "newPassword"); @@ -1248,8 +1153,9 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService); + ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM)); backupManagerService.hasBackupPassword(); @@ -1272,8 +1178,9 @@ public class BackupManagerServiceTest { */ @Test public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); expectThrows( @@ -1299,9 +1206,10 @@ public class BackupManagerServiceTest { */ @Test public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); + ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); @@ -1335,8 +1243,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); @@ -1370,8 +1278,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); @@ -1408,8 +1316,9 @@ public class BackupManagerServiceTest { */ @Test public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); expectThrows( @@ -1422,9 +1331,9 @@ public class BackupManagerServiceTest { */ @Test public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + registerUser(backupManagerService, mUserTwoId, mUserTwoService); ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); @@ -1436,8 +1345,8 @@ public class BackupManagerServiceTest { /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); @@ -1449,8 +1358,8 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest(); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); @@ -1459,12 +1368,18 @@ public class BackupManagerServiceTest { verify(mUserOneService, never()).adbRestore(parcelFileDescriptor); } + private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception { + File testFile = new File(mContext.getFilesDir(), "test"); + testFile.createNewFile(); + return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE); + } + /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); @@ -1489,8 +1404,8 @@ public class BackupManagerServiceTest { @Test public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); @@ -1519,24 +1434,22 @@ public class BackupManagerServiceTest { @Test public void testDump_onRegisteredUser_callsMethodForUser() throws Exception { grantDumpPermissions(); - - BackupManagerService backupManagerService = - createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); File testFile = createTestFile(); FileDescriptor fileDescriptor = new FileDescriptor(); PrintWriter printWriter = new PrintWriter(testFile); String[] args = {"1", "2"}; + ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM)); backupManagerService.dump(fileDescriptor, printWriter, args); - verify(mUserOneService).dump(fileDescriptor, printWriter, args); + verify(mUserSystemService).dump(fileDescriptor, printWriter, args); } /** Test that the backup service does not route methods for non-registered users. */ @Test public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception { grantDumpPermissions(); - BackupManagerService backupManagerService = createService(); File testFile = createTestFile(); FileDescriptor fileDescriptor = new FileDescriptor(); @@ -1552,9 +1465,8 @@ public class BackupManagerServiceTest { @Test public void testDump_users_dumpsListOfRegisteredUsers() { grantDumpPermissions(); - - BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, - mUserOneService); + BackupManagerService backupManagerService = createSystemRegisteredService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); StringWriter out = new StringWriter(); PrintWriter writer = new PrintWriter(out); String[] args = {"users"}; @@ -1563,31 +1475,162 @@ public class BackupManagerServiceTest { writer.flush(); assertEquals( - String.format("%s %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE, - mUserOneId), + String.format("%s %d %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE, + UserHandle.USER_SYSTEM, mUserOneId), out.toString()); } + private File createTestFile() throws IOException { + File testFile = new File(mContext.getFilesDir(), "test"); + testFile.createNewFile(); + return testFile; + } + private void grantDumpPermissions() { mShadowContext.grantPermissions(DUMP); mShadowContext.grantPermissions(PACKAGE_USAGE_STATS); } - private File createTestFile() throws IOException { - File testFile = new File(mContext.getFilesDir(), "test"); - testFile.createNewFile(); - return testFile; + /** + * Test that the backup services throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + expectThrows( + SecurityException.class, + () -> + backupManagerService.getServiceForUserIfCallerHasPermission( + mUserOneId, "test")); + } + + /** + * Test that the backup services does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() { + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true); + + assertEquals( + mUserOneService, + backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test")); + } + + /** + * Test that the backup services does not throw a {@link SecurityException} if the caller does + * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id. + */ + @Test + public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() { + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + assertEquals( + mUserOneService, + backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test")); + } + + /** + * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is + * specifically to prevent overloading the logs in production. + */ + @Test + public void testMoreDebug_isFalse() throws Exception { + boolean moreDebug = BackupManagerService.MORE_DEBUG; + + assertThat(moreDebug).isFalse(); + } + + /** Test that the constructor handles {@code null} parameters. */ + @Test + public void testConstructor_withNullContext_throws() throws Exception { + expectThrows( + NullPointerException.class, + () -> + new BackupManagerService( + /* context */ null, + new SparseArray<>())); + } + + /** Test that the constructor does not create {@link UserBackupManagerService} instances. */ + @Test + public void testConstructor_doesNotRegisterUsers() throws Exception { + BackupManagerService backupManagerService = createService(); + + assertThat(backupManagerService.getUserServices().size()).isEqualTo(0); + } + + // --------------------------------------------- + // Lifecycle tests + // --------------------------------------------- + + /** testOnStart_publishesService */ + @Test + public void testOnStart_publishesService() { + BackupManagerService backupManagerService = mock(BackupManagerService.class); + BackupManagerService.Lifecycle lifecycle = + spy(new BackupManagerService.Lifecycle(mContext, backupManagerService)); + doNothing().when(lifecycle).publishService(anyString(), any()); + + lifecycle.onStart(); + + verify(lifecycle).publishService(Context.BACKUP_SERVICE, backupManagerService); + } + + /** testOnUnlockUser_forwards */ + @Test + public void testOnUnlockUser_forwards() { + BackupManagerService backupManagerService = mock(BackupManagerService.class); + BackupManagerService.Lifecycle lifecycle = + new BackupManagerService.Lifecycle(mContext, backupManagerService); + + lifecycle.onUnlockUser(UserHandle.USER_SYSTEM); + + verify(backupManagerService).onUnlockUser(UserHandle.USER_SYSTEM); + } + + /** testOnStopUser_forwards */ + @Test + public void testOnStopUser_forwards() { + BackupManagerService backupManagerService = mock(BackupManagerService.class); + BackupManagerService.Lifecycle lifecycle = + new BackupManagerService.Lifecycle(mContext, backupManagerService); + + lifecycle.onStopUser(UserHandle.USER_SYSTEM); + + verify(backupManagerService).onStopUser(UserHandle.USER_SYSTEM); } private BackupManagerService createService() { - mShadowContext.grantPermissions(BACKUP); - return new BackupManagerService( - mContext, new Trampoline(mContext), startBackupThread(null)); + return new BackupManagerService(mContext); + } + + private BackupManagerService createSystemRegisteredService() { + BackupManagerService backupManagerService = createService(); + registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserSystemService); + return backupManagerService; + } + + private void registerUser( + BackupManagerService backupManagerService, + int userId, + UserBackupManagerService userBackupManagerService) { + backupManagerService.setBackupServiceActive(userId, true); + backupManagerService.startServiceForUser(userId, userBackupManagerService); } private BackupManagerService createServiceAndRegisterUser( int userId, UserBackupManagerService userBackupManagerService) { BackupManagerService backupManagerService = createService(); + backupManagerService.setBackupServiceActive(userBackupManagerService.getUserId(), true); backupManagerService.startServiceForUser(userId, userBackupManagerService); return backupManagerService; } @@ -1606,10 +1649,4 @@ public class BackupManagerServiceTest { mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL); } } - - private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception { - File testFile = new File(mContext.getFilesDir(), "test"); - testFile.createNewFile(); - return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE); - } } diff --git a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java index 9a78d0b3f456..dbc0da707477 100644 --- a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java +++ b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java @@ -25,6 +25,8 @@ import android.os.Handler; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import com.android.server.testing.shadows.ShadowSystemServiceRegistry; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -36,7 +38,7 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowJobScheduler; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowJobScheduler.class}) +@Config(shadows = {ShadowJobScheduler.class, ShadowSystemServiceRegistry.class}) @Presubmit public class FullBackupJobTest { private Context mContext; diff --git a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java index 8d9e44fbf1ad..1c5fac28de3c 100644 --- a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java +++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java @@ -24,14 +24,18 @@ import android.os.Handler; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import com.android.server.testing.shadows.ShadowSystemServiceRegistry; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowSystemServiceRegistry.class}) @Presubmit public class KeyValueBackupJobTest { private Context mContext; diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java index 74fe81c6f68e..dfe75ed50cd4 100644 --- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -30,10 +30,13 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.expectThrows; import android.app.backup.BackupManager; @@ -63,6 +66,7 @@ import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowBinder; import com.android.server.testing.shadows.ShadowKeyValueBackupJob; import com.android.server.testing.shadows.ShadowKeyValueBackupTask; +import com.android.server.testing.shadows.ShadowSystemServiceRegistry; import org.junit.After; import org.junit.Before; @@ -80,7 +84,12 @@ import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowPackageManager; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -88,12 +97,18 @@ import java.util.List; * UserBackupManagerService} that performs operations for its target user. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowAppBackupUtils.class, ShadowApplicationPackageManager.class}) +@Config( + shadows = { + ShadowAppBackupUtils.class, + ShadowApplicationPackageManager.class, + ShadowSystemServiceRegistry.class + }) @Presubmit public class UserBackupManagerServiceTest { private static final String TAG = "BMSTest"; private static final String PACKAGE_1 = "some.package.1"; private static final String PACKAGE_2 = "some.package.2"; + private static final String USER_FACING_PACKAGE = "user.facing.package"; private static final int USER_ID = 10; @Mock private TransportManager mTransportManager; @@ -510,6 +525,23 @@ public class UserBackupManagerServiceTest { expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent); } + /** + * Test verifying that {@link UserBackupManagerService#excludeKeysFromRestore(String, List)} + * throws a {@link SecurityException} if the caller does not have backup permission. + */ + @Test + public void testExcludeKeysFromRestore_withoutPermission() throws Exception { + mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); + UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks(); + + expectThrows( + SecurityException.class, + () -> + backupManagerService.excludeKeysFromRestore( + PACKAGE_1, + new ArrayList<String>(){})); + } + /* Tests for updating transport attributes */ private static final int PACKAGE_UID = 10; @@ -999,7 +1031,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService.createAndInitializeService( USER_ID, mContext, - new Trampoline(mContext), + new BackupManagerService(mContext), mBackupThread, mBaseStateDir, mDataDir, @@ -1020,7 +1052,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService.createAndInitializeService( USER_ID, mContext, - new Trampoline(mContext), + new BackupManagerService(mContext), mBackupThread, mBaseStateDir, mDataDir, @@ -1039,7 +1071,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService.createAndInitializeService( USER_ID, /* context */ null, - new Trampoline(mContext), + new BackupManagerService(mContext), mBackupThread, mBaseStateDir, mDataDir, @@ -1071,7 +1103,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService.createAndInitializeService( USER_ID, mContext, - new Trampoline(mContext), + new BackupManagerService(mContext), /* backupThread */ null, mBaseStateDir, mDataDir, @@ -1087,7 +1119,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService.createAndInitializeService( USER_ID, mContext, - new Trampoline(mContext), + new BackupManagerService(mContext), mBackupThread, /* baseStateDir */ null, mDataDir, @@ -1096,8 +1128,8 @@ public class UserBackupManagerServiceTest { /** * Test checking non-null argument on {@link - * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread, - * File, File, TransportManager)}. + * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService, + * HandlerThread, File, File, TransportManager)}. */ @Test public void testCreateAndInitializeService_withNullDataDir_throws() { @@ -1107,7 +1139,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService.createAndInitializeService( USER_ID, mContext, - new Trampoline(mContext), + new BackupManagerService(mContext), mBackupThread, mBaseStateDir, /* dataDir */ null, @@ -1116,8 +1148,8 @@ public class UserBackupManagerServiceTest { /** * Test checking non-null argument on {@link - * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread, - * File, File, TransportManager)}. + * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService, + * HandlerThread, File, File, TransportManager)}. */ @Test public void testCreateAndInitializeService_withNullTransportManager_throws() { @@ -1127,7 +1159,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService.createAndInitializeService( USER_ID, mContext, - new Trampoline(mContext), + new BackupManagerService(mContext), mBackupThread, mBaseStateDir, mDataDir, @@ -1145,7 +1177,7 @@ public class UserBackupManagerServiceTest { UserBackupManagerService service = UserBackupManagerService.createAndInitializeService( USER_ID, contextSpy, - new Trampoline(mContext), + new BackupManagerService(mContext), mBackupThread, mBaseStateDir, mDataDir, @@ -1159,6 +1191,47 @@ public class UserBackupManagerServiceTest { eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any()); } + @Test + public void testFilterUserFacingPackages_shouldSkipUserFacing_filtersUserFacing() { + List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE), + getPackageInfo(PACKAGE_1)); + UserBackupManagerService backupManagerService = spy( + createUserBackupManagerServiceAndRunTasks()); + when(backupManagerService.shouldSkipUserFacingData()).thenReturn(true); + when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true); + + List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages( + packages); + + assertFalse(containsPackage(filteredPackages, USER_FACING_PACKAGE)); + assertTrue(containsPackage(filteredPackages, PACKAGE_1)); + } + + @Test + public void testFilterUserFacingPackages_shouldNotSkipUserFacing_doesNotFilterUserFacing() { + List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE), + getPackageInfo(PACKAGE_1)); + UserBackupManagerService backupManagerService = spy( + createUserBackupManagerServiceAndRunTasks()); + when(backupManagerService.shouldSkipUserFacingData()).thenReturn(false); + when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true); + + List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages( + packages); + + assertTrue(containsPackage(filteredPackages, USER_FACING_PACKAGE)); + assertTrue(containsPackage(filteredPackages, PACKAGE_1)); + } + + private static boolean containsPackage(List<PackageInfo> packages, String targetPackage) { + for (PackageInfo packageInfo : packages) { + if (targetPackage.equals(packageInfo.packageName)) { + return true; + } + } + return false; + } + private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() { return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks( USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager); @@ -1232,13 +1305,49 @@ public class UserBackupManagerServiceTest { assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2); } + /** + * Test that {@link UserBackupManagerService#dump()} for system user does not prefix dump with + * "User 0:". + */ + @Test + public void testDump_forSystemUser_DoesNotHaveUserPrefix() throws Exception { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + UserBackupManagerService service = + BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks( + UserHandle.USER_SYSTEM, + mContext, + mBackupThread, + mBaseStateDir, + mDataDir, + mTransportManager); + + StringWriter dump = new StringWriter(); + service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]); + + assertThat(dump.toString()).startsWith("Backup Manager is "); + } + + /** + * Test that {@link UserBackupManagerService#dump()} for non-system user prefixes dump with + * "User <userid>:". + */ + @Test + public void testDump_forNonSystemUser_HasUserPrefix() throws Exception { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks(); + + StringWriter dump = new StringWriter(); + service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]); + + assertThat(dump.toString()).startsWith("User " + USER_ID + ":" + "Backup Manager is "); + } + private File createTestFile() throws IOException { File testFile = new File(mContext.getFilesDir(), "test"); testFile.createNewFile(); return testFile; } - /** * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method. diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java deleted file mode 100644 index 3f57240bc0e9..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunk; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import com.google.common.primitives.Bytes; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.util.Arrays; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class ChunkHashTest { - private static final int HASH_LENGTH_BYTES = 256 / 8; - private static final byte[] TEST_HASH_1 = Arrays.copyOf(new byte[] {1}, HASH_LENGTH_BYTES); - private static final byte[] TEST_HASH_2 = Arrays.copyOf(new byte[] {2}, HASH_LENGTH_BYTES); - - @Test - public void testGetHash_returnsHash() { - ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); - - byte[] hash = chunkHash.getHash(); - - assertThat(hash).asList().containsExactlyElementsIn(Bytes.asList(TEST_HASH_1)).inOrder(); - } - - @Test - public void testEquals() { - ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); - ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); - ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); - - assertThat(chunkHash1).isEqualTo(equalChunkHash1); - assertThat(chunkHash1).isNotEqualTo(chunkHash2); - } - - @Test - public void testHashCode() { - ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); - ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); - ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); - - int hash1 = chunkHash1.hashCode(); - int equalHash1 = equalChunkHash1.hashCode(); - int hash2 = chunkHash2.hashCode(); - - assertThat(hash1).isEqualTo(equalHash1); - assertThat(hash1).isNotEqualTo(hash2); - } - - @Test - public void testCompareTo_whenEqual_returnsZero() { - ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); - ChunkHash equalChunkHash = new ChunkHash(TEST_HASH_1); - - int result = chunkHash.compareTo(equalChunkHash); - - assertThat(result).isEqualTo(0); - } - - @Test - public void testCompareTo_whenArgumentGreater_returnsNegative() { - ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); - ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); - - int result = chunkHash1.compareTo(chunkHash2); - - assertThat(result).isLessThan(0); - } - - @Test - public void testCompareTo_whenArgumentSmaller_returnsPositive() { - ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); - ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); - - int result = chunkHash2.compareTo(chunkHash1); - - assertThat(result).isGreaterThan(0); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java deleted file mode 100644 index 24e5573b891d..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunk; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; -import android.util.proto.ProtoInputStream; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.util.Preconditions; - -import com.google.common.base.Charsets; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.io.ByteArrayInputStream; -import java.util.Arrays; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class ChunkListingMapTest { - private static final String CHUNK_A = "CHUNK_A"; - private static final String CHUNK_B = "CHUNK_B"; - private static final String CHUNK_C = "CHUNK_C"; - - private static final int CHUNK_A_LENGTH = 256; - private static final int CHUNK_B_LENGTH = 1024; - private static final int CHUNK_C_LENGTH = 4055; - - private ChunkHash mChunkHashA; - private ChunkHash mChunkHashB; - private ChunkHash mChunkHashC; - - @Before - public void setUp() throws Exception { - mChunkHashA = getHash(CHUNK_A); - mChunkHashB = getHash(CHUNK_B); - mChunkHashC = getHash(CHUNK_C); - } - - @Test - public void testHasChunk_whenChunkInListing_returnsTrue() throws Exception { - byte[] chunkListingProto = - createChunkListingProto( - new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, - new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - ChunkListingMap chunkListingMap = - ChunkListingMap.readFromProto( - new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - - boolean chunkAInList = chunkListingMap.hasChunk(mChunkHashA); - boolean chunkBInList = chunkListingMap.hasChunk(mChunkHashB); - boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC); - - assertThat(chunkAInList).isTrue(); - assertThat(chunkBInList).isTrue(); - assertThat(chunkCInList).isTrue(); - } - - @Test - public void testHasChunk_whenChunkNotInListing_returnsFalse() throws Exception { - byte[] chunkListingProto = - createChunkListingProto( - new ChunkHash[] {mChunkHashA, mChunkHashB}, - new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH}); - ChunkListingMap chunkListingMap = - ChunkListingMap.readFromProto( - new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - ChunkHash chunkHashEmpty = getHash(""); - - boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC); - boolean emptyChunkInList = chunkListingMap.hasChunk(chunkHashEmpty); - - assertThat(chunkCInList).isFalse(); - assertThat(emptyChunkInList).isFalse(); - } - - @Test - public void testGetChunkEntry_returnsEntryWithCorrectLength() throws Exception { - byte[] chunkListingProto = - createChunkListingProto( - new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, - new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - ChunkListingMap chunkListingMap = - ChunkListingMap.readFromProto( - new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - - ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA); - ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB); - ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC); - - assertThat(entryA.getLength()).isEqualTo(CHUNK_A_LENGTH); - assertThat(entryB.getLength()).isEqualTo(CHUNK_B_LENGTH); - assertThat(entryC.getLength()).isEqualTo(CHUNK_C_LENGTH); - } - - @Test - public void testGetChunkEntry_returnsEntryWithCorrectStart() throws Exception { - byte[] chunkListingProto = - createChunkListingProto( - new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, - new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - ChunkListingMap chunkListingMap = - ChunkListingMap.readFromProto( - new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - - ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA); - ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB); - ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC); - - assertThat(entryA.getStart()).isEqualTo(0); - assertThat(entryB.getStart()).isEqualTo(CHUNK_A_LENGTH); - assertThat(entryC.getStart()).isEqualTo(CHUNK_A_LENGTH + CHUNK_B_LENGTH); - } - - @Test - public void testGetChunkEntry_returnsNullForNonExistentChunk() throws Exception { - byte[] chunkListingProto = - createChunkListingProto( - new ChunkHash[] {mChunkHashA, mChunkHashB}, - new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH}); - ChunkListingMap chunkListingMap = - ChunkListingMap.readFromProto( - new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - - ChunkListingMap.Entry chunkEntryNonexistentChunk = - chunkListingMap.getChunkEntry(mChunkHashC); - - assertThat(chunkEntryNonexistentChunk).isNull(); - } - - @Test - public void testReadFromProto_whenEmptyProto_returnsChunkListingMapWith0Chunks() - throws Exception { - ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {})); - - ChunkListingMap chunkListingMap = ChunkListingMap.readFromProto(emptyProto); - - assertThat(chunkListingMap.getChunkCount()).isEqualTo(0); - } - - @Test - public void testReadFromProto_returnsChunkListingWithCorrectSize() throws Exception { - byte[] chunkListingProto = - createChunkListingProto( - new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, - new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); - - ChunkListingMap chunkListingMap = - ChunkListingMap.readFromProto( - new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); - - assertThat(chunkListingMap.getChunkCount()).isEqualTo(3); - } - - private byte[] createChunkListingProto(ChunkHash[] hashes, int[] lengths) { - Preconditions.checkArgument(hashes.length == lengths.length); - ProtoOutputStream outputStream = new ProtoOutputStream(); - - for (int i = 0; i < hashes.length; ++i) { - writeToProtoOutputStream(outputStream, hashes[i], lengths[i]); - } - outputStream.flush(); - - return outputStream.getBytes(); - } - - private void writeToProtoOutputStream(ProtoOutputStream out, ChunkHash chunkHash, int length) { - long token = out.start(ChunksMetadataProto.ChunkListing.CHUNKS); - out.write(ChunksMetadataProto.Chunk.HASH, chunkHash.getHash()); - out.write(ChunksMetadataProto.Chunk.LENGTH, length); - out.end(token); - } - - private ChunkHash getHash(String name) { - return new ChunkHash( - Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES)); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java deleted file mode 100644 index 17c9a86169be..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunk; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; -import android.util.proto.ProtoInputStream; -import android.util.proto.ProtoOutputStream; - -import com.google.common.base.Charsets; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.io.ByteArrayInputStream; -import java.util.Arrays; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class ChunkTest { - private static final String CHUNK_A = "CHUNK_A"; - private static final int CHUNK_A_LENGTH = 256; - - private ChunkHash mChunkHashA; - - @Before - public void setUp() throws Exception { - mChunkHashA = getHash(CHUNK_A); - } - - @Test - public void testReadFromProto_readsCorrectly() throws Exception { - ProtoOutputStream out = new ProtoOutputStream(); - out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash()); - out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH); - out.flush(); - byte[] protoBytes = out.getBytes(); - - Chunk chunk = - Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); - - assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash()); - assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH); - } - - @Test - public void testReadFromProto_whenFieldsWrittenInReversedOrder_readsCorrectly() - throws Exception { - ProtoOutputStream out = new ProtoOutputStream(); - // Write fields of Chunk proto in reverse order. - out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH); - out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash()); - out.flush(); - byte[] protoBytes = out.getBytes(); - - Chunk chunk = - Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); - - assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash()); - assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH); - } - - @Test - public void testReadFromProto_whenEmptyProto_returnsEmptyHash() throws Exception { - ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {})); - - Chunk chunk = Chunk.readFromProto(emptyProto); - - assertThat(chunk.getHash()).asList().hasSize(0); - assertThat(chunk.getLength()).isEqualTo(0); - } - - @Test - public void testReadFromProto_whenOnlyHashSet_returnsChunkWithOnlyHash() throws Exception { - ProtoOutputStream out = new ProtoOutputStream(); - out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash()); - out.flush(); - byte[] protoBytes = out.getBytes(); - - Chunk chunk = - Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); - - assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash()); - assertThat(chunk.getLength()).isEqualTo(0); - } - - @Test - public void testReadFromProto_whenOnlyLengthSet_returnsChunkWithOnlyLength() throws Exception { - ProtoOutputStream out = new ProtoOutputStream(); - out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH); - out.flush(); - byte[] protoBytes = out.getBytes(); - - Chunk chunk = - Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); - - assertThat(chunk.getHash()).isEqualTo(new byte[] {}); - assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH); - } - - private ChunkHash getHash(String name) { - return new ChunkHash( - Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES)); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java deleted file mode 100644 index 0bf14174e5c3..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunk; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import com.google.common.primitives.Bytes; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class EncryptedChunkOrderingTest { - private static final byte[] TEST_BYTE_ARRAY_1 = new byte[] {1, 2, 3, 4, 5}; - private static final byte[] TEST_BYTE_ARRAY_2 = new byte[] {5, 4, 3, 2, 1}; - - @Test - public void testEncryptedChunkOrdering_returnsValue() { - EncryptedChunkOrdering encryptedChunkOrdering = - EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); - - byte[] bytes = encryptedChunkOrdering.encryptedChunkOrdering(); - - assertThat(bytes) - .asList() - .containsExactlyElementsIn(Bytes.asList(TEST_BYTE_ARRAY_1)) - .inOrder(); - } - - @Test - public void testEquals() { - EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); - EncryptedChunkOrdering equalChunkOrdering1 = - EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); - EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2); - - assertThat(chunkOrdering1).isEqualTo(equalChunkOrdering1); - assertThat(chunkOrdering1).isNotEqualTo(chunkOrdering2); - } - - @Test - public void testHashCode() { - EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); - EncryptedChunkOrdering equalChunkOrdering1 = - EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); - EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2); - - int hash1 = chunkOrdering1.hashCode(); - int equalHash1 = equalChunkOrdering1.hashCode(); - int hash2 = chunkOrdering2.hashCode(); - - assertThat(hash1).isEqualTo(equalHash1); - assertThat(hash1).isNotEqualTo(hash2); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java deleted file mode 100644 index 8df08262c9fa..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking; - -import static org.junit.Assert.assertEquals; -import static org.testng.Assert.assertThrows; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -/** Tests for {@link ByteRange}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class ByteRangeTest { - @Test - public void getLength_includesEnd() throws Exception { - ByteRange byteRange = new ByteRange(5, 10); - - int length = byteRange.getLength(); - - assertEquals(6, length); - } - - @Test - public void constructor_rejectsNegativeStart() { - assertThrows(IllegalArgumentException.class, () -> new ByteRange(-1, 10)); - } - - @Test - public void constructor_rejectsEndBeforeStart() { - assertThrows(IllegalArgumentException.class, () -> new ByteRange(10, 9)); - } - - @Test - public void extend_withZeroLength_throwsException() { - ByteRange byteRange = new ByteRange(5, 10); - - assertThrows(IllegalArgumentException.class, () -> byteRange.extend(0)); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java deleted file mode 100644 index d0e5fb335da9..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunking; - -import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import android.platform.test.annotations.Presubmit; - -import com.android.server.backup.encryption.chunk.ChunkHash; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.RobolectricTestRunner; - -import java.security.SecureRandom; - -import javax.crypto.Cipher; -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.GCMParameterSpec; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class ChunkEncryptorTest { - private static final String MAC_ALGORITHM = "HmacSHA256"; - private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding"; - private static final int GCM_NONCE_LENGTH_BYTES = 12; - private static final int GCM_TAG_LENGTH_BYTES = 16; - private static final String CHUNK_PLAINTEXT = - "A little Learning is a dang'rous Thing;\n" - + "Drink deep, or taste not the Pierian Spring:\n" - + "There shallow Draughts intoxicate the Brain,\n" - + "And drinking largely sobers us again."; - private static final byte[] PLAINTEXT_BYTES = CHUNK_PLAINTEXT.getBytes(UTF_8); - private static final byte[] NONCE_1 = "0123456789abc".getBytes(UTF_8); - private static final byte[] NONCE_2 = "123456789abcd".getBytes(UTF_8); - - private static final byte[][] NONCES = new byte[][] {NONCE_1, NONCE_2}; - - @Mock private SecureRandom mSecureRandomMock; - private SecretKey mSecretKey; - private ChunkHash mPlaintextHash; - private ChunkEncryptor mChunkEncryptor; - - @Before - public void setUp() throws Exception { - mSecretKey = generateAesKey(); - ChunkHasher chunkHasher = new ChunkHasher(mSecretKey); - mPlaintextHash = chunkHasher.computeHash(PLAINTEXT_BYTES); - mSecureRandomMock = mock(SecureRandom.class); - mChunkEncryptor = new ChunkEncryptor(mSecretKey, mSecureRandomMock); - - // Return NONCE_1, then NONCE_2 for invocations of mSecureRandomMock.nextBytes(). - doAnswer( - new Answer<Void>() { - private int mInvocation = 0; - - @Override - public Void answer(InvocationOnMock invocation) { - byte[] nonceDestination = invocation.getArgument(0); - System.arraycopy( - NONCES[this.mInvocation], - 0, - nonceDestination, - 0, - GCM_NONCE_LENGTH_BYTES); - this.mInvocation++; - return null; - } - }) - .when(mSecureRandomMock) - .nextBytes(any(byte[].class)); - } - - @Test - public void encrypt_withHash_resultContainsHashAsKey() throws Exception { - EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - assertThat(chunk.key()).isEqualTo(mPlaintextHash); - } - - @Test - public void encrypt_generatesHmacOfPlaintext() throws Exception { - EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - byte[] generatedHash = chunk.key().getHash(); - Mac mac = Mac.getInstance(MAC_ALGORITHM); - mac.init(mSecretKey); - byte[] plaintextHmac = mac.doFinal(PLAINTEXT_BYTES); - assertThat(generatedHash).isEqualTo(plaintextHmac); - } - - @Test - public void encrypt_whenInvokedAgain_generatesNewNonce() throws Exception { - EncryptedChunk chunk1 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - EncryptedChunk chunk2 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - assertThat(chunk1.nonce()).isNotEqualTo(chunk2.nonce()); - } - - @Test - public void encrypt_whenInvokedAgain_generatesNewCiphertext() throws Exception { - EncryptedChunk chunk1 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - EncryptedChunk chunk2 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - assertThat(chunk1.encryptedBytes()).isNotEqualTo(chunk2.encryptedBytes()); - } - - @Test - public void encrypt_generates12ByteNonce() throws Exception { - EncryptedChunk encryptedChunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - byte[] nonce = encryptedChunk.nonce(); - assertThat(nonce).hasLength(GCM_NONCE_LENGTH_BYTES); - } - - @Test - public void encrypt_decryptedResultCorrespondsToPlaintext() throws Exception { - EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES); - - Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); - cipher.init( - Cipher.DECRYPT_MODE, - mSecretKey, - new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * 8, chunk.nonce())); - byte[] decrypted = cipher.doFinal(chunk.encryptedBytes()); - assertThat(decrypted).isEqualTo(PLAINTEXT_BYTES); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java deleted file mode 100644 index 2bbbf2857146..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunking; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import com.android.server.backup.encryption.chunk.ChunkHash; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class ChunkHasherTest { - private static final String KEY_ALGORITHM = "AES"; - private static final String MAC_ALGORITHM = "HmacSHA256"; - - private static final byte[] TEST_KEY = {100, 120}; - private static final byte[] TEST_DATA = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; - - private SecretKey mSecretKey; - private ChunkHasher mChunkHasher; - - @Before - public void setUp() throws Exception { - mSecretKey = new SecretKeySpec(TEST_KEY, KEY_ALGORITHM); - mChunkHasher = new ChunkHasher(mSecretKey); - } - - @Test - public void computeHash_returnsHmacForData() throws Exception { - ChunkHash chunkHash = mChunkHasher.computeHash(TEST_DATA); - - byte[] hash = chunkHash.getHash(); - Mac mac = Mac.getInstance(MAC_ALGORITHM); - mac.init(mSecretKey); - byte[] expectedHash = mac.doFinal(TEST_DATA); - assertThat(hash).isEqualTo(expectedHash); - } - - @Test - public void computeHash_generates256BitHmac() throws Exception { - int expectedLength = 256 / Byte.SIZE; - - byte[] hash = mChunkHasher.computeHash(TEST_DATA).getHash(); - - assertThat(hash).hasLength(expectedLength); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java deleted file mode 100644 index 2af6f2bee8ff..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.platform.test.annotations.Presubmit; - -import com.google.common.primitives.Bytes; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.robolectric.RobolectricTestRunner; - -import java.io.IOException; - -/** Tests for {@link DiffScriptBackupWriter}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class DiffScriptBackupWriterTest { - private static final byte[] TEST_BYTES = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - - @Captor private ArgumentCaptor<Byte> mBytesCaptor; - @Mock private SingleStreamDiffScriptWriter mDiffScriptWriter; - private BackupWriter mBackupWriter; - - @Before - public void setUp() { - mDiffScriptWriter = mock(SingleStreamDiffScriptWriter.class); - mBackupWriter = new DiffScriptBackupWriter(mDiffScriptWriter); - mBytesCaptor = ArgumentCaptor.forClass(Byte.class); - } - - @Test - public void writeBytes_writesBytesToWriter() throws Exception { - mBackupWriter.writeBytes(TEST_BYTES); - - verify(mDiffScriptWriter, atLeastOnce()).writeByte(mBytesCaptor.capture()); - assertThat(mBytesCaptor.getAllValues()) - .containsExactlyElementsIn(Bytes.asList(TEST_BYTES)) - .inOrder(); - } - - @Test - public void writeChunk_writesChunkToWriter() throws Exception { - mBackupWriter.writeChunk(0, 10); - - verify(mDiffScriptWriter).writeChunk(0, 10); - } - - @Test - public void getBytesWritten_returnsTotalSum() throws Exception { - mBackupWriter.writeBytes(TEST_BYTES); - mBackupWriter.writeBytes(TEST_BYTES); - mBackupWriter.writeChunk(/*start=*/ 0, /*length=*/ 10); - - long bytesWritten = mBackupWriter.getBytesWritten(); - - assertThat(bytesWritten).isEqualTo(2 * TEST_BYTES.length + 10); - } - - @Test - public void flush_flushesWriter() throws IOException { - mBackupWriter.flush(); - - verify(mDiffScriptWriter).flush(); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java deleted file mode 100644 index 8e801a133909..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunking; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.platform.test.annotations.Presubmit; - -import com.android.server.backup.encryption.chunk.ChunkHash; - -import com.google.common.primitives.Bytes; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.util.Arrays; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class EncryptedChunkTest { - private static final byte[] CHUNK_HASH_1_BYTES = - Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES); - private static final byte[] NONCE_1 = - Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES); - private static final byte[] ENCRYPTED_BYTES_1 = - Arrays.copyOf(new byte[] {3}, EncryptedChunk.KEY_LENGTH_BYTES); - - private static final byte[] CHUNK_HASH_2_BYTES = - Arrays.copyOf(new byte[] {4}, ChunkHash.HASH_LENGTH_BYTES); - private static final byte[] NONCE_2 = - Arrays.copyOf(new byte[] {5}, EncryptedChunk.NONCE_LENGTH_BYTES); - private static final byte[] ENCRYPTED_BYTES_2 = - Arrays.copyOf(new byte[] {6}, EncryptedChunk.KEY_LENGTH_BYTES); - - @Test - public void testCreate_withIncorrectLength_throwsException() { - ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES); - byte[] shortNonce = Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES - 1); - - assertThrows( - IllegalArgumentException.class, - () -> EncryptedChunk.create(chunkHash, shortNonce, ENCRYPTED_BYTES_1)); - } - - @Test - public void testEncryptedBytes_forNewlyCreatedObject_returnsCorrectValue() { - ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES); - EncryptedChunk encryptedChunk = - EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1); - - byte[] returnedBytes = encryptedChunk.encryptedBytes(); - - assertThat(returnedBytes) - .asList() - .containsExactlyElementsIn(Bytes.asList(ENCRYPTED_BYTES_1)) - .inOrder(); - } - - @Test - public void testKey_forNewlyCreatedObject_returnsCorrectValue() { - ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES); - EncryptedChunk encryptedChunk = - EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1); - - ChunkHash returnedKey = encryptedChunk.key(); - - assertThat(returnedKey).isEqualTo(chunkHash); - } - - @Test - public void testNonce_forNewlycreatedObject_returnCorrectValue() { - ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES); - EncryptedChunk encryptedChunk = - EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1); - - byte[] returnedNonce = encryptedChunk.nonce(); - - assertThat(returnedNonce).asList().containsExactlyElementsIn(Bytes.asList(NONCE_1)); - } - - @Test - public void testEquals() { - ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES); - ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES); - ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES); - EncryptedChunk encryptedChunk1 = - EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1); - EncryptedChunk equalEncryptedChunk1 = - EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1); - EncryptedChunk encryptedChunk2 = - EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2); - - assertThat(encryptedChunk1).isEqualTo(equalEncryptedChunk1); - assertThat(encryptedChunk1).isNotEqualTo(encryptedChunk2); - } - - @Test - public void testHashCode() { - ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES); - ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES); - ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES); - EncryptedChunk encryptedChunk1 = - EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1); - EncryptedChunk equalEncryptedChunk1 = - EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1); - EncryptedChunk encryptedChunk2 = - EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2); - - int hash1 = encryptedChunk1.hashCode(); - int equalHash1 = equalEncryptedChunk1.hashCode(); - int hash2 = encryptedChunk2.hashCode(); - - assertThat(hash1).isEqualTo(equalHash1); - assertThat(hash1).isNotEqualTo(hash2); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java deleted file mode 100644 index 2f872beacd17..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunking; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; - -import android.platform.test.annotations.Presubmit; - -import com.android.server.backup.encryption.chunk.ChunkHash; -import com.android.server.backup.encryption.chunk.ChunksMetadataProto; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.robolectric.RobolectricTestRunner; - -import java.util.Arrays; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class InlineLengthsEncryptedChunkEncoderTest { - - private static final byte[] TEST_NONCE = - Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES); - private static final byte[] TEST_KEY_DATA = - Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES); - private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9}; - - @Mock private BackupWriter mMockBackupWriter; - private ChunkHash mTestKey; - private EncryptedChunk mTestChunk; - private EncryptedChunkEncoder mEncoder; - - @Before - public void setUp() throws Exception { - mMockBackupWriter = mock(BackupWriter.class); - mTestKey = new ChunkHash(TEST_KEY_DATA); - mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA); - mEncoder = new InlineLengthsEncryptedChunkEncoder(); - } - - @Test - public void writeChunkToWriter_writesLengthThenNonceThenData() throws Exception { - mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk); - - InOrder inOrder = inOrder(mMockBackupWriter); - inOrder.verify(mMockBackupWriter) - .writeBytes( - InlineLengthsEncryptedChunkEncoder.toByteArray( - TEST_NONCE.length + TEST_DATA.length)); - inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE); - inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA); - } - - @Test - public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() { - int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk); - - assertThat(encodedLength).isEqualTo(Integer.BYTES + TEST_NONCE.length + TEST_DATA.length); - } - - @Test - public void getChunkOrderingType_returnsExplicitStartsType() { - assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.INLINE_LENGTHS); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java deleted file mode 100644 index 978bddb7301a..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunking; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; - -import android.platform.test.annotations.Presubmit; - -import com.android.server.backup.encryption.chunk.ChunkHash; -import com.android.server.backup.encryption.chunk.ChunksMetadataProto; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.robolectric.RobolectricTestRunner; - -import java.util.Arrays; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class LengthlessEncryptedChunkEncoderTest { - private static final byte[] TEST_NONCE = - Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES); - private static final byte[] TEST_KEY_DATA = - Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES); - private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9}; - - @Mock private BackupWriter mMockBackupWriter; - private ChunkHash mTestKey; - private EncryptedChunk mTestChunk; - private EncryptedChunkEncoder mEncoder; - - @Before - public void setUp() throws Exception { - mMockBackupWriter = mock(BackupWriter.class); - mTestKey = new ChunkHash(TEST_KEY_DATA); - mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA); - mEncoder = new LengthlessEncryptedChunkEncoder(); - } - - @Test - public void writeChunkToWriter_writesNonceThenData() throws Exception { - mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk); - - InOrder inOrder = inOrder(mMockBackupWriter); - inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE); - inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA); - } - - @Test - public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() { - int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk); - - assertThat(encodedLength).isEqualTo(TEST_NONCE.length + TEST_DATA.length); - } - - @Test - public void getChunkOrderingType_returnsExplicitStartsType() { - assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.EXPLICIT_STARTS); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java deleted file mode 100644 index 19ef8fb339ba..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.encryption.chunking; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.testng.Assert.assertThrows; - -import android.platform.test.annotations.Presubmit; - -import com.google.common.primitives.Bytes; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.io.ByteArrayOutputStream; - -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class RawBackupWriterTest { - private static final byte[] TEST_BYTES = {1, 2, 3, 4, 5, 6}; - - private BackupWriter mWriter; - private ByteArrayOutputStream mOutput; - - @Before - public void setUp() { - mOutput = new ByteArrayOutputStream(); - mWriter = new RawBackupWriter(mOutput); - } - - @Test - public void writeBytes_writesToOutputStream() throws Exception { - mWriter.writeBytes(TEST_BYTES); - - assertThat(mOutput.toByteArray()) - .asList() - .containsExactlyElementsIn(Bytes.asList(TEST_BYTES)) - .inOrder(); - } - - @Test - public void writeChunk_throwsUnsupportedOperationException() throws Exception { - assertThrows(UnsupportedOperationException.class, () -> mWriter.writeChunk(0, 0)); - } - - @Test - public void getBytesWritten_returnsTotalSum() throws Exception { - mWriter.writeBytes(TEST_BYTES); - mWriter.writeBytes(TEST_BYTES); - - long bytesWritten = mWriter.getBytesWritten(); - - assertThat(bytesWritten).isEqualTo(2 * TEST_BYTES.length); - } - - @Test - public void flush_flushesOutputStream() throws Exception { - mOutput = mock(ByteArrayOutputStream.class); - mWriter = new RawBackupWriter(mOutput); - - mWriter.flush(); - - verify(mOutput).flush(); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java deleted file mode 100644 index 73baf80a2c70..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.testng.Assert.assertThrows; - -import android.platform.test.annotations.Presubmit; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Locale; - -/** Tests for {@link SingleStreamDiffScriptWriter}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class SingleStreamDiffScriptWriterTest { - private static final int MAX_CHUNK_SIZE_IN_BYTES = 256; - /** By default this Locale does not use Arabic numbers for %d formatting. */ - private static final Locale HINDI = new Locale("hi", "IN"); - - private Locale mDefaultLocale; - private ByteArrayOutputStream mOutputStream; - private SingleStreamDiffScriptWriter mDiffScriptWriter; - - @Before - public void setUp() { - mDefaultLocale = Locale.getDefault(); - mOutputStream = new ByteArrayOutputStream(); - mDiffScriptWriter = - new SingleStreamDiffScriptWriter(mOutputStream, MAX_CHUNK_SIZE_IN_BYTES); - } - - @After - public void tearDown() { - Locale.setDefault(mDefaultLocale); - } - - @Test - public void writeChunk_withNegativeStart_throwsException() { - assertThrows( - IllegalArgumentException.class, - () -> mDiffScriptWriter.writeChunk(-1, 50)); - } - - @Test - public void writeChunk_withZeroLength_throwsException() { - assertThrows( - IllegalArgumentException.class, - () -> mDiffScriptWriter.writeChunk(0, 0)); - } - - @Test - public void writeChunk_withExistingBytesInBuffer_writesBufferFirst() - throws IOException { - String testString = "abcd"; - writeStringAsBytesToWriter(testString, mDiffScriptWriter); - - mDiffScriptWriter.writeChunk(0, 20); - mDiffScriptWriter.flush(); - - // Expected format: length of abcd, newline, abcd, newline, chunk start - chunk end - assertThat(mOutputStream.toString("UTF-8")).isEqualTo( - String.format("%d\n%s\n%d-%d\n", testString.length(), testString, 0, 19)); - } - - @Test - public void writeChunk_overlappingPreviousChunk_combinesChunks() throws IOException { - mDiffScriptWriter.writeChunk(3, 4); - - mDiffScriptWriter.writeChunk(7, 5); - mDiffScriptWriter.flush(); - - assertThat(mOutputStream.toString("UTF-8")).isEqualTo(String.format("3-11\n")); - } - - @Test - public void writeChunk_formatsByteIndexesUsingArabicNumbers() throws Exception { - Locale.setDefault(HINDI); - - mDiffScriptWriter.writeChunk(0, 12345); - mDiffScriptWriter.flush(); - - assertThat(mOutputStream.toString("UTF-8")).isEqualTo("0-12344\n"); - } - - @Test - public void flush_flushesOutputStream() throws IOException { - ByteArrayOutputStream mockOutputStream = mock(ByteArrayOutputStream.class); - SingleStreamDiffScriptWriter diffScriptWriter = - new SingleStreamDiffScriptWriter(mockOutputStream, MAX_CHUNK_SIZE_IN_BYTES); - - diffScriptWriter.flush(); - - verify(mockOutputStream).flush(); - } - - private void writeStringAsBytesToWriter(String string, SingleStreamDiffScriptWriter writer) - throws IOException { - byte[] bytes = string.getBytes("UTF-8"); - for (int i = 0; i < bytes.length; i++) { - writer.writeByte(bytes[i]); - } - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java deleted file mode 100644 index 77b734785424..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking.cdc; - -import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.security.GeneralSecurityException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Random; - -import javax.crypto.SecretKey; - -/** Tests for {@link ContentDefinedChunker}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class ContentDefinedChunkerTest { - private static final int WINDOW_SIZE_BYTES = 31; - private static final int MIN_SIZE_BYTES = 40; - private static final int MAX_SIZE_BYTES = 300; - private static final String CHUNK_BOUNDARY = "<----------BOUNDARY----------->"; - private static final byte[] CHUNK_BOUNDARY_BYTES = CHUNK_BOUNDARY.getBytes(UTF_8); - private static final String CHUNK_1 = "This is the first chunk"; - private static final String CHUNK_2 = "And this is the second chunk"; - private static final String CHUNK_3 = "And finally here is the third chunk"; - private static final String SMALL_CHUNK = "12345678"; - - private FingerprintMixer mFingerprintMixer; - private RabinFingerprint64 mRabinFingerprint64; - private ContentDefinedChunker mChunker; - - /** Set up a {@link ContentDefinedChunker} and dependencies for use in the tests. */ - @Before - public void setUp() throws Exception { - SecretKey secretKey = generateAesKey(); - byte[] salt = new byte[FingerprintMixer.SALT_LENGTH_BYTES]; - Random random = new Random(); - random.nextBytes(salt); - mFingerprintMixer = new FingerprintMixer(secretKey, salt); - - mRabinFingerprint64 = new RabinFingerprint64(); - long chunkBoundaryFingerprint = calculateFingerprint(CHUNK_BOUNDARY_BYTES); - mChunker = - new ContentDefinedChunker( - MIN_SIZE_BYTES, - MAX_SIZE_BYTES, - mRabinFingerprint64, - mFingerprintMixer, - (fingerprint) -> fingerprint == chunkBoundaryFingerprint); - } - - /** - * Creating a {@link ContentDefinedChunker} with a minimum chunk size that is smaller than the - * window size should throw an {@link IllegalArgumentException}. - */ - @Test - public void create_withMinChunkSizeSmallerThanWindowSize_throwsIllegalArgumentException() { - assertThrows( - IllegalArgumentException.class, - () -> - new ContentDefinedChunker( - WINDOW_SIZE_BYTES - 1, - MAX_SIZE_BYTES, - mRabinFingerprint64, - mFingerprintMixer, - null)); - } - - /** - * Creating a {@link ContentDefinedChunker} with a maximum chunk size that is smaller than the - * minimum chunk size should throw an {@link IllegalArgumentException}. - */ - @Test - public void create_withMaxChunkSizeSmallerThanMinChunkSize_throwsIllegalArgumentException() { - assertThrows( - IllegalArgumentException.class, - () -> - new ContentDefinedChunker( - MIN_SIZE_BYTES, - MIN_SIZE_BYTES - 1, - mRabinFingerprint64, - mFingerprintMixer, - null)); - } - - /** - * {@link ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should split the - * input stream across chunk boundaries by default. - */ - @Test - public void chunkify_withLargeChunks_splitsIntoChunksAcrossBoundaries() throws Exception { - byte[] input = - (CHUNK_1 + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY + CHUNK_3).getBytes(UTF_8); - ByteArrayInputStream inputStream = new ByteArrayInputStream(input); - ArrayList<String> result = new ArrayList<>(); - - mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8))); - - assertThat(result) - .containsExactly(CHUNK_1 + CHUNK_BOUNDARY, CHUNK_2 + CHUNK_BOUNDARY, CHUNK_3) - .inOrder(); - } - - /** Chunks should be combined across boundaries until they reach the minimum chunk size. */ - @Test - public void chunkify_withSmallChunks_combinesChunksUntilMinSize() throws Exception { - byte[] input = - (SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY + CHUNK_3).getBytes(UTF_8); - ByteArrayInputStream inputStream = new ByteArrayInputStream(input); - ArrayList<String> result = new ArrayList<>(); - - mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8))); - - assertThat(result) - .containsExactly(SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY, CHUNK_3) - .inOrder(); - assertThat(result.get(0).length()).isAtLeast(MIN_SIZE_BYTES); - } - - /** Chunks can not be larger than the maximum chunk size. */ - @Test - public void chunkify_doesNotProduceChunksLargerThanMaxSize() throws Exception { - byte[] largeInput = new byte[MAX_SIZE_BYTES * 10]; - Arrays.fill(largeInput, "a".getBytes(UTF_8)[0]); - ByteArrayInputStream inputStream = new ByteArrayInputStream(largeInput); - ArrayList<String> result = new ArrayList<>(); - - mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8))); - - byte[] expectedChunkBytes = new byte[MAX_SIZE_BYTES]; - Arrays.fill(expectedChunkBytes, "a".getBytes(UTF_8)[0]); - String expectedChunk = new String(expectedChunkBytes, UTF_8); - assertThat(result) - .containsExactly( - expectedChunk, - expectedChunk, - expectedChunk, - expectedChunk, - expectedChunk, - expectedChunk, - expectedChunk, - expectedChunk, - expectedChunk, - expectedChunk) - .inOrder(); - } - - /** - * If the input stream signals zero availablility, {@link - * ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should still work. - */ - @Test - public void chunkify_withInputStreamReturningZeroAvailability_returnsChunks() throws Exception { - byte[] input = (SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2).getBytes(UTF_8); - ZeroAvailabilityInputStream zeroAvailabilityInputStream = - new ZeroAvailabilityInputStream(input); - ArrayList<String> result = new ArrayList<>(); - - mChunker.chunkify( - zeroAvailabilityInputStream, (chunk) -> result.add(new String(chunk, UTF_8))); - - assertThat(result).containsExactly(SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2).inOrder(); - } - - /** - * {@link ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should rethrow any - * exception thrown by its consumer. - */ - @Test - public void chunkify_whenConsumerThrowsException_rethrowsException() throws Exception { - ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {1}); - - assertThrows( - GeneralSecurityException.class, - () -> - mChunker.chunkify( - inputStream, - (chunk) -> { - throw new GeneralSecurityException(); - })); - } - - private long calculateFingerprint(byte[] bytes) { - long fingerprint = 0; - for (byte inByte : bytes) { - fingerprint = - mRabinFingerprint64.computeFingerprint64( - /*inChar=*/ inByte, /*outChar=*/ (byte) 0, fingerprint); - } - return mFingerprintMixer.mix(fingerprint); - } - - private static class ZeroAvailabilityInputStream extends ByteArrayInputStream { - ZeroAvailabilityInputStream(byte[] wrapped) { - super(wrapped); - } - - @Override - public synchronized int available() { - return 0; - } - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java deleted file mode 100644 index 936b5dca033d..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking.cdc; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.security.InvalidKeyException; -import java.security.Key; -import java.util.HashSet; -import java.util.Random; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -/** Tests for {@link FingerprintMixer}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class FingerprintMixerTest { - private static final String KEY_ALGORITHM = "AES"; - private static final int SEED = 42; - private static final int SALT_LENGTH_BYTES = 256 / 8; - private static final int KEY_SIZE_BITS = 256; - - private Random mSeededRandom; - private FingerprintMixer mFingerprintMixer; - - /** Set up a {@link FingerprintMixer} with deterministic key and salt generation. */ - @Before - public void setUp() throws Exception { - // Seed so that the tests are deterministic. - mSeededRandom = new Random(SEED); - mFingerprintMixer = new FingerprintMixer(randomKey(), randomSalt()); - } - - /** - * Construcing a {@link FingerprintMixer} with a salt that is too small should throw an {@link - * IllegalArgumentException}. - */ - @Test - public void create_withIncorrectSaltSize_throwsIllegalArgumentException() { - byte[] tooSmallSalt = new byte[SALT_LENGTH_BYTES - 1]; - - assertThrows( - IllegalArgumentException.class, - () -> new FingerprintMixer(randomKey(), tooSmallSalt)); - } - - /** - * Constructing a {@link FingerprintMixer} with a secret key that can't be encoded should throw - * an {@link InvalidKeyException}. - */ - @Test - public void create_withUnencodableSecretKey_throwsInvalidKeyException() { - byte[] keyBytes = new byte[KEY_SIZE_BITS / 8]; - UnencodableSecretKeySpec keySpec = - new UnencodableSecretKeySpec(keyBytes, 0, keyBytes.length, KEY_ALGORITHM); - - assertThrows(InvalidKeyException.class, () -> new FingerprintMixer(keySpec, randomSalt())); - } - - /** - * {@link FingerprintMixer#getAddend()} should not return the same addend for two different - * keys. - */ - @Test - public void getAddend_withDifferentKey_returnsDifferentResult() throws Exception { - int iterations = 100_000; - HashSet<Long> returnedAddends = new HashSet<>(); - byte[] salt = randomSalt(); - - for (int i = 0; i < iterations; i++) { - FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), salt); - long addend = fingerprintMixer.getAddend(); - returnedAddends.add(addend); - } - - assertThat(returnedAddends).containsNoDuplicates(); - } - - /** - * {@link FingerprintMixer#getMultiplicand()} should not return the same multiplicand for two - * different keys. - */ - @Test - public void getMultiplicand_withDifferentKey_returnsDifferentResult() throws Exception { - int iterations = 100_000; - HashSet<Long> returnedMultiplicands = new HashSet<>(); - byte[] salt = randomSalt(); - - for (int i = 0; i < iterations; i++) { - FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), salt); - long multiplicand = fingerprintMixer.getMultiplicand(); - returnedMultiplicands.add(multiplicand); - } - - assertThat(returnedMultiplicands).containsNoDuplicates(); - } - - /** The multiplicant returned by {@link FingerprintMixer} should always be odd. */ - @Test - public void getMultiplicand_isOdd() throws Exception { - int iterations = 100_000; - - for (int i = 0; i < iterations; i++) { - FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), randomSalt()); - - long multiplicand = fingerprintMixer.getMultiplicand(); - - assertThat(isOdd(multiplicand)).isTrue(); - } - } - - /** {@link FingerprintMixer#mix(long)} should have a random distribution. */ - @Test - public void mix_randomlyDistributesBits() throws Exception { - int iterations = 100_000; - float tolerance = 0.1f; - int[] totals = new int[64]; - - for (int i = 0; i < iterations; i++) { - long n = mFingerprintMixer.mix(mSeededRandom.nextLong()); - for (int j = 0; j < 64; j++) { - int bit = (int) (n >> j & 1); - totals[j] += bit; - } - } - - for (int i = 0; i < 64; i++) { - float mean = ((float) totals[i]) / iterations; - float diff = Math.abs(mean - 0.5f); - assertThat(diff).isLessThan(tolerance); - } - } - - /** - * {@link FingerprintMixer#mix(long)} should always produce a number that's different from the - * input. - */ - @Test - public void mix_doesNotProduceSameNumberAsInput() { - int iterations = 100_000; - - for (int i = 0; i < iterations; i++) { - assertThat(mFingerprintMixer.mix(i)).isNotEqualTo(i); - } - } - - private byte[] randomSalt() { - byte[] salt = new byte[SALT_LENGTH_BYTES]; - mSeededRandom.nextBytes(salt); - return salt; - } - - /** - * Not a secure way of generating keys. We want to deterministically generate the same keys for - * each test run, though, to ensure the test is deterministic. - */ - private SecretKey randomKey() { - byte[] keyBytes = new byte[KEY_SIZE_BITS / 8]; - mSeededRandom.nextBytes(keyBytes); - return new SecretKeySpec(keyBytes, 0, keyBytes.length, KEY_ALGORITHM); - } - - private static boolean isOdd(long n) { - return Math.abs(n % 2) == 1; - } - - /** - * Subclass of {@link SecretKeySpec} that does not provide an encoded version. As per its - * contract in {@link Key}, that means {@code getEncoded()} always returns null. - */ - private class UnencodableSecretKeySpec extends SecretKeySpec { - UnencodableSecretKeySpec(byte[] key, int offset, int len, String algorithm) { - super(key, offset, len, algorithm); - } - - @Override - public byte[] getEncoded() { - return null; - } - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java deleted file mode 100644 index 549437454e9c..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking.cdc; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -/** Tests for {@link Hkdf}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class HkdfTest { - /** HKDF Test Case 1 IKM from RFC 5869 */ - private static final byte[] HKDF_CASE1_IKM = { - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b - }; - - /** HKDF Test Case 1 salt from RFC 5869 */ - private static final byte[] HKDF_CASE1_SALT = { - 0x00, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0x0c - }; - - /** HKDF Test Case 1 info from RFC 5869 */ - private static final byte[] HKDF_CASE1_INFO = { - (byte) 0xf0, (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0xf4, - (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9 - }; - - /** First 32 bytes of HKDF Test Case 1 OKM (output) from RFC 5869 */ - private static final byte[] HKDF_CASE1_OKM = { - (byte) 0x3c, (byte) 0xb2, (byte) 0x5f, (byte) 0x25, (byte) 0xfa, - (byte) 0xac, (byte) 0xd5, (byte) 0x7a, (byte) 0x90, (byte) 0x43, - (byte) 0x4f, (byte) 0x64, (byte) 0xd0, (byte) 0x36, (byte) 0x2f, - (byte) 0x2a, (byte) 0x2d, (byte) 0x2d, (byte) 0x0a, (byte) 0x90, - (byte) 0xcf, (byte) 0x1a, (byte) 0x5a, (byte) 0x4c, (byte) 0x5d, - (byte) 0xb0, (byte) 0x2d, (byte) 0x56, (byte) 0xec, (byte) 0xc4, - (byte) 0xc5, (byte) 0xbf - }; - - /** Test the example from RFC 5869. */ - @Test - public void hkdf_derivesKeyMaterial() throws Exception { - byte[] result = Hkdf.hkdf(HKDF_CASE1_IKM, HKDF_CASE1_SALT, HKDF_CASE1_INFO); - - assertThat(result).isEqualTo(HKDF_CASE1_OKM); - } - - /** Providing a key that is null should throw a {@link java.lang.NullPointerException}. */ - @Test - public void hkdf_withNullKey_throwsNullPointerException() throws Exception { - assertThrows( - NullPointerException.class, - () -> Hkdf.hkdf(null, HKDF_CASE1_SALT, HKDF_CASE1_INFO)); - } - - /** Providing a salt that is null should throw a {@link java.lang.NullPointerException}. */ - @Test - public void hkdf_withNullSalt_throwsNullPointerException() throws Exception { - assertThrows( - NullPointerException.class, () -> Hkdf.hkdf(HKDF_CASE1_IKM, null, HKDF_CASE1_INFO)); - } - - /** Providing data that is null should throw a {@link java.lang.NullPointerException}. */ - @Test - public void hkdf_withNullData_throwsNullPointerException() throws Exception { - assertThrows( - NullPointerException.class, () -> Hkdf.hkdf(HKDF_CASE1_IKM, HKDF_CASE1_SALT, null)); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java deleted file mode 100644 index 277dc372e73c..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking.cdc; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.util.Random; - -/** Tests for {@link IsChunkBreakpoint}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class IsChunkBreakpointTest { - private static final int RANDOM_SEED = 42; - private static final double TOLERANCE = 0.01; - private static final int NUMBER_OF_TESTS = 10000; - private static final int BITS_PER_LONG = 64; - - private Random mRandom; - - /** Make sure that tests are deterministic. */ - @Before - public void setUp() { - mRandom = new Random(RANDOM_SEED); - } - - /** - * Providing a negative average number of trials should throw an {@link - * IllegalArgumentException}. - */ - @Test - public void create_withNegativeAverageNumberOfTrials_throwsIllegalArgumentException() { - assertThrows(IllegalArgumentException.class, () -> new IsChunkBreakpoint(-1)); - } - - // Note: the following three tests are compute-intensive, so be cautious adding more. - - /** - * If the provided average number of trials is zero, a breakpoint should be expected after one - * trial on average. - */ - @Test - public void - isBreakpoint_withZeroAverageNumberOfTrials_isTrueOnAverageAfterOneTrial() { - assertExpectedTrials(new IsChunkBreakpoint(0), /*expectedTrials=*/ 1); - } - - /** - * If the provided average number of trials is 512, a breakpoint should be expected after 512 - * trials on average. - */ - @Test - public void - isBreakpoint_with512AverageNumberOfTrials_isTrueOnAverageAfter512Trials() { - assertExpectedTrials(new IsChunkBreakpoint(512), /*expectedTrials=*/ 512); - } - - /** - * If the provided average number of trials is 1024, a breakpoint should be expected after 1024 - * trials on average. - */ - @Test - public void - isBreakpoint_with1024AverageNumberOfTrials_isTrueOnAverageAfter1024Trials() { - assertExpectedTrials(new IsChunkBreakpoint(1024), /*expectedTrials=*/ 1024); - } - - /** The number of leading zeros should be the logarithm of the average number of trials. */ - @Test - public void getLeadingZeros_squaredIsAverageNumberOfTrials() { - for (int i = 0; i < BITS_PER_LONG; i++) { - long averageNumberOfTrials = (long) Math.pow(2, i); - - int leadingZeros = new IsChunkBreakpoint(averageNumberOfTrials).getLeadingZeros(); - - assertThat(leadingZeros).isEqualTo(i); - } - } - - private void assertExpectedTrials(IsChunkBreakpoint isChunkBreakpoint, long expectedTrials) { - long sum = 0; - for (int i = 0; i < NUMBER_OF_TESTS; i++) { - sum += numberOfTrialsTillBreakpoint(isChunkBreakpoint); - } - long averageTrials = sum / NUMBER_OF_TESTS; - assertThat((double) Math.abs(averageTrials - expectedTrials)) - .isLessThan(TOLERANCE * expectedTrials); - } - - private int numberOfTrialsTillBreakpoint(IsChunkBreakpoint isChunkBreakpoint) { - int trials = 0; - - while (true) { - trials++; - if (isChunkBreakpoint.isBreakpoint(mRandom.nextLong())) { - return trials; - } - } - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java deleted file mode 100644 index 729580cf5101..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.chunking.cdc; - -import static com.google.common.truth.Truth.assertThat; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import android.platform.test.annotations.Presubmit; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -/** Tests for {@link RabinFingerprint64}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class RabinFingerprint64Test { - private static final int WINDOW_SIZE = 31; - private static final ImmutableList<String> TEST_STRINGS = - ImmutableList.of( - "ervHTtChYXO6eXivYqThlyyzqkbRaOR", - "IxaVunH9ZC3qneWfhj1GkBH4ys9CYqz", - "wZRVjlE1p976icCFPX9pibk4PEBvjSH", - "pHIVaT8x8If9D6s9croksgNmJpmGYWI"); - - private final RabinFingerprint64 mRabinFingerprint64 = new RabinFingerprint64(); - - /** - * No matter where in the input buffer a string occurs, {@link - * RabinFingerprint64#computeFingerprint64(byte, byte, long)} should return the same - * fingerprint. - */ - @Test - public void computeFingerprint64_forSameWindow_returnsSameFingerprint() { - long fingerprint1 = - computeFingerprintAtPosition(getBytes(TEST_STRINGS.get(0)), WINDOW_SIZE - 1); - long fingerprint2 = - computeFingerprintAtPosition( - getBytes(TEST_STRINGS.get(1), TEST_STRINGS.get(0)), WINDOW_SIZE * 2 - 1); - long fingerprint3 = - computeFingerprintAtPosition( - getBytes(TEST_STRINGS.get(2), TEST_STRINGS.get(3), TEST_STRINGS.get(0)), - WINDOW_SIZE * 3 - 1); - String stub = "abc"; - long fingerprint4 = - computeFingerprintAtPosition( - getBytes(stub, TEST_STRINGS.get(0)), WINDOW_SIZE + stub.length() - 1); - - // Assert that all fingerprints are exactly the same - assertThat(ImmutableSet.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4)) - .hasSize(1); - } - - /** The computed fingerprint should be different for different inputs. */ - @Test - public void computeFingerprint64_withDifferentInput_returnsDifferentFingerprint() { - long fingerprint1 = computeFingerprintOf(TEST_STRINGS.get(0)); - long fingerprint2 = computeFingerprintOf(TEST_STRINGS.get(1)); - long fingerprint3 = computeFingerprintOf(TEST_STRINGS.get(2)); - long fingerprint4 = computeFingerprintOf(TEST_STRINGS.get(3)); - - assertThat(ImmutableList.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4)) - .containsNoDuplicates(); - } - - /** - * An input with the same characters in a different order should return a different fingerprint. - */ - @Test - public void computeFingerprint64_withSameInputInDifferentOrder_returnsDifferentFingerprint() { - long fingerprint1 = computeFingerprintOf("abcdefghijklmnopqrstuvwxyz12345"); - long fingerprint2 = computeFingerprintOf("54321zyxwvutsrqponmlkjihgfedcba"); - long fingerprint3 = computeFingerprintOf("4bcdefghijklmnopqrstuvwxyz123a5"); - long fingerprint4 = computeFingerprintOf("bacdefghijklmnopqrstuvwxyz12345"); - - assertThat(ImmutableList.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4)) - .containsNoDuplicates(); - } - - /** UTF-8 bytes of all the given strings in order. */ - private byte[] getBytes(String... strings) { - StringBuilder sb = new StringBuilder(); - for (String s : strings) { - sb.append(s); - } - return sb.toString().getBytes(UTF_8); - } - - /** - * The Rabin fingerprint of a window of bytes ending at {@code position} in the {@code bytes} - * array. - */ - private long computeFingerprintAtPosition(byte[] bytes, int position) { - assertThat(position).isAtMost(bytes.length - 1); - long fingerprint = 0; - for (int i = 0; i <= position; i++) { - byte outChar; - if (i >= WINDOW_SIZE) { - outChar = bytes[i - WINDOW_SIZE]; - } else { - outChar = (byte) 0; - } - fingerprint = - mRabinFingerprint64.computeFingerprint64( - /*inChar=*/ bytes[i], outChar, fingerprint); - } - return fingerprint; - } - - private long computeFingerprintOf(String s) { - assertThat(s.length()).isEqualTo(WINDOW_SIZE); - return computeFingerprintAtPosition(s.getBytes(UTF_8), WINDOW_SIZE - 1); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java deleted file mode 100644 index 5342efa18a97..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.keys; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.content.Context; -import android.platform.test.annotations.Presubmit; -import android.security.keystore.recovery.InternalRecoveryServiceException; -import android.security.keystore.recovery.RecoveryController; - -import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException; -import com.android.server.testing.shadows.ShadowRecoveryController; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -import java.security.SecureRandom; -import java.util.Optional; - -/** Tests for {@link RecoverableKeyStoreSecondaryKeyManager}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class}) -public class RecoverableKeyStoreSecondaryKeyManagerTest { - private static final String BACKUP_KEY_ALIAS_PREFIX = - "com.android.server.backup/recoverablekeystore/"; - private static final int BITS_PER_BYTE = 8; - private static final int BACKUP_KEY_SUFFIX_LENGTH_BYTES = 128 / BITS_PER_BYTE; - private static final int HEX_PER_BYTE = 2; - private static final int BACKUP_KEY_ALIAS_LENGTH = - BACKUP_KEY_ALIAS_PREFIX.length() + BACKUP_KEY_SUFFIX_LENGTH_BYTES * HEX_PER_BYTE; - private static final String NONEXISTENT_KEY_ALIAS = "NONEXISTENT_KEY_ALIAS"; - - private RecoverableKeyStoreSecondaryKeyManager mRecoverableKeyStoreSecondaryKeyManager; - private Context mContext; - - /** Create a new {@link RecoverableKeyStoreSecondaryKeyManager} to use in tests. */ - @Before - public void setUp() throws Exception { - mContext = RuntimeEnvironment.application; - - mRecoverableKeyStoreSecondaryKeyManager = - new RecoverableKeyStoreSecondaryKeyManager( - RecoveryController.getInstance(mContext), new SecureRandom()); - } - - /** Reset the {@link ShadowRecoveryController}. */ - @After - public void tearDown() throws Exception { - ShadowRecoveryController.reset(); - } - - /** The generated key should always have the prefix {@code BACKUP_KEY_ALIAS_PREFIX}. */ - @Test - public void generate_generatesKeyWithExpectedPrefix() throws Exception { - RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); - - assertThat(key.getAlias()).startsWith(BACKUP_KEY_ALIAS_PREFIX); - } - - /** The generated key should always have length {@code BACKUP_KEY_ALIAS_LENGTH}. */ - @Test - public void generate_generatesKeyWithExpectedLength() throws Exception { - RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); - - assertThat(key.getAlias()).hasLength(BACKUP_KEY_ALIAS_LENGTH); - } - - /** Ensure that hidden API exceptions are rethrown when generating keys. */ - @Test - public void generate_encounteringHiddenApiException_rethrowsException() { - ShadowRecoveryController.setThrowsInternalError(true); - - assertThrows( - InternalRecoveryServiceException.class, - mRecoverableKeyStoreSecondaryKeyManager::generate); - } - - /** Ensure that retrieved keys correspond to those generated earlier. */ - @Test - public void get_getsKeyGeneratedByController() throws Exception { - RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); - - Optional<RecoverableKeyStoreSecondaryKey> retrievedKey = - mRecoverableKeyStoreSecondaryKeyManager.get(key.getAlias()); - - assertThat(retrievedKey.isPresent()).isTrue(); - assertThat(retrievedKey.get().getAlias()).isEqualTo(key.getAlias()); - assertThat(retrievedKey.get().getSecretKey()).isEqualTo(key.getSecretKey()); - } - - /** - * Ensure that a call to {@link RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} - * for nonexistent aliases returns an emtpy {@link Optional}. - */ - @Test - public void get_forNonExistentKey_returnsEmptyOptional() throws Exception { - Optional<RecoverableKeyStoreSecondaryKey> retrievedKey = - mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS); - - assertThat(retrievedKey.isPresent()).isFalse(); - } - - /** - * Ensure that exceptions occurring during {@link - * RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} are not rethrown. - */ - @Test - public void get_encounteringInternalException_doesNotPropagateException() throws Exception { - ShadowRecoveryController.setThrowsInternalError(true); - - // Should not throw exception - mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS); - } - - /** Ensure that keys are correctly removed from the store. */ - @Test - public void remove_removesKeyFromRecoverableStore() throws Exception { - RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate(); - - mRecoverableKeyStoreSecondaryKeyManager.remove(key.getAlias()); - - assertThat(RecoveryController.getInstance(mContext).getAliases()) - .doesNotContain(key.getAlias()); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java deleted file mode 100644 index 89977f82c145..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.keys; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.content.Context; -import android.platform.test.annotations.Presubmit; -import android.security.keystore.recovery.RecoveryController; - -import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey.Status; -import com.android.server.backup.testing.CryptoTestUtils; -import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException; -import com.android.server.testing.shadows.ShadowRecoveryController; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -import javax.crypto.SecretKey; - -/** Tests for {@link RecoverableKeyStoreSecondaryKey}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class}) -public class RecoverableKeyStoreSecondaryKeyTest { - private static final String TEST_ALIAS = "test"; - private static final int NONEXISTENT_STATUS_CODE = 42; - - private RecoverableKeyStoreSecondaryKey mSecondaryKey; - private SecretKey mGeneratedSecretKey; - private Context mContext; - - /** Instantiate a {@link RecoverableKeyStoreSecondaryKey} to use in tests. */ - @Before - public void setUp() throws Exception { - mContext = RuntimeEnvironment.application; - mGeneratedSecretKey = CryptoTestUtils.generateAesKey(); - mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, mGeneratedSecretKey); - } - - /** Reset the {@link ShadowRecoveryController}. */ - @After - public void tearDown() throws Exception { - ShadowRecoveryController.reset(); - } - - /** - * Checks that {@link RecoverableKeyStoreSecondaryKey#getAlias()} returns the value supplied in - * the constructor. - */ - @Test - public void getAlias() { - String alias = mSecondaryKey.getAlias(); - - assertThat(alias).isEqualTo(TEST_ALIAS); - } - - /** - * Checks that {@link RecoverableKeyStoreSecondaryKey#getSecretKey()} returns the value supplied - * in the constructor. - */ - @Test - public void getSecretKey() { - SecretKey secretKey = mSecondaryKey.getSecretKey(); - - assertThat(secretKey).isEqualTo(mGeneratedSecretKey); - } - - /** - * Checks that passing a secret key that is null to the constructor throws an exception. - */ - @Test - public void constructor_withNullSecretKey_throwsNullPointerException() { - assertThrows( - NullPointerException.class, - () -> new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, null)); - } - - /** - * Checks that passing an alias that is null to the constructor throws an exception. - */ - @Test - public void constructor_withNullAlias_throwsNullPointerException() { - assertThrows( - NullPointerException.class, - () -> new RecoverableKeyStoreSecondaryKey(null, mGeneratedSecretKey)); - } - - /** Checks that the synced status is returned correctly. */ - @Test - public void getStatus_whenSynced_returnsSynced() throws Exception { - setStatus(RecoveryController.RECOVERY_STATUS_SYNCED); - - int status = mSecondaryKey.getStatus(mContext); - - assertThat(status).isEqualTo(Status.SYNCED); - } - - /** Checks that the in progress sync status is returned correctly. */ - @Test - public void getStatus_whenNotSynced_returnsNotSynced() throws Exception { - setStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); - - int status = mSecondaryKey.getStatus(mContext); - - assertThat(status).isEqualTo(Status.NOT_SYNCED); - } - - /** Checks that the failure status is returned correctly. */ - @Test - public void getStatus_onPermanentFailure_returnsDestroyed() throws Exception { - setStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE); - - int status = mSecondaryKey.getStatus(mContext); - - assertThat(status).isEqualTo(Status.DESTROYED); - } - - /** Checks that an unknown status results in {@code NOT_SYNCED} being returned. */ - @Test - public void getStatus_forUnknownStatusCode_returnsNotSynced() throws Exception { - setStatus(NONEXISTENT_STATUS_CODE); - - int status = mSecondaryKey.getStatus(mContext); - - assertThat(status).isEqualTo(Status.NOT_SYNCED); - } - - /** Checks that an internal error results in {@code NOT_SYNCED} being returned. */ - @Test - public void getStatus_onInternalError_returnsNotSynced() throws Exception { - ShadowRecoveryController.setThrowsInternalError(true); - - int status = mSecondaryKey.getStatus(mContext); - - assertThat(status).isEqualTo(Status.NOT_SYNCED); - } - - private void setStatus(int status) throws Exception { - ShadowRecoveryController.setRecoveryStatus(TEST_ALIAS, status); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java deleted file mode 100644 index 48216f8d7aca..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.keys; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.security.SecureRandom; - -import javax.crypto.SecretKey; - -/** Tests for {@link TertiaryKeyGenerator}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class TertiaryKeyGeneratorTest { - private static final String KEY_ALGORITHM = "AES"; - private static final int KEY_SIZE_BITS = 256; - - private TertiaryKeyGenerator mTertiaryKeyGenerator; - - /** Instantiate a new {@link TertiaryKeyGenerator} for use in tests. */ - @Before - public void setUp() { - mTertiaryKeyGenerator = new TertiaryKeyGenerator(new SecureRandom()); - } - - /** Generated keys should be AES keys. */ - @Test - public void generate_generatesAESKeys() { - SecretKey secretKey = mTertiaryKeyGenerator.generate(); - - assertThat(secretKey.getAlgorithm()).isEqualTo(KEY_ALGORITHM); - } - - /** Generated keys should be 256 bits in size. */ - @Test - public void generate_generates256BitKeys() { - SecretKey secretKey = mTertiaryKeyGenerator.generate(); - - assertThat(secretKey.getEncoded()).hasLength(KEY_SIZE_BITS / 8); - } - - /** - * Subsequent calls to {@link TertiaryKeyGenerator#generate()} should generate different keys. - */ - @Test - public void generate_generatesNewKeys() { - SecretKey key1 = mTertiaryKeyGenerator.generate(); - SecretKey key2 = mTertiaryKeyGenerator.generate(); - - assertThat(key1).isNotEqualTo(key2); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java deleted file mode 100644 index 49bb410ceb65..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.keys; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -/** Tests for {@link TertiaryKeyRotationTracker}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class TertiaryKeyRotationTrackerTest { - private static final String PACKAGE_1 = "com.package.one"; - private static final int NUMBER_OF_BACKUPS_BEFORE_ROTATION = 31; - - private TertiaryKeyRotationTracker mTertiaryKeyRotationTracker; - - /** Instantiate a {@link TertiaryKeyRotationTracker} for use in tests. */ - @Before - public void setUp() { - mTertiaryKeyRotationTracker = newInstance(); - } - - /** New packages should not be due for key rotation. */ - @Test - public void isKeyRotationDue_forNewPackage_isFalse() { - // Simulate a new package by not calling simulateBackups(). As a result, PACKAGE_1 hasn't - // been seen by mTertiaryKeyRotationTracker before. - boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1); - - assertThat(keyRotationDue).isFalse(); - } - - /** - * Key rotation should not be due after less than {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} - * backups. - */ - @Test - public void isKeyRotationDue_afterLessThanRotationAmountBackups_isFalse() { - simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION - 1); - - boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1); - - assertThat(keyRotationDue).isFalse(); - } - - /** Key rotation should be due after {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} backups. */ - @Test - public void isKeyRotationDue_afterRotationAmountBackups_isTrue() { - simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION); - - boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1); - - assertThat(keyRotationDue).isTrue(); - } - - /** - * A call to {@link TertiaryKeyRotationTracker#resetCountdown(String)} should make sure no key - * rotation is due. - */ - @Test - public void resetCountdown_makesKeyRotationNotDue() { - simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION); - - mTertiaryKeyRotationTracker.resetCountdown(PACKAGE_1); - - assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse(); - } - - /** - * New instances of {@link TertiaryKeyRotationTracker} should read state about the number of - * backups from disk. - */ - @Test - public void isKeyRotationDue_forNewInstance_readsStateFromDisk() { - simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION); - - boolean keyRotationDueForNewInstance = newInstance().isKeyRotationDue(PACKAGE_1); - - assertThat(keyRotationDueForNewInstance).isTrue(); - } - - /** - * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should mark all previously - * seen packages for rotation. - */ - @Test - public void markAllForRotation_marksSeenPackagesForKeyRotation() { - simulateBackups(PACKAGE_1, /*numberOfBackups=*/ 1); - - mTertiaryKeyRotationTracker.markAllForRotation(); - - assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isTrue(); - } - - /** - * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should not mark any new - * packages for rotation. - */ - @Test - public void markAllForRotation_doesNotMarkUnseenPackages() { - mTertiaryKeyRotationTracker.markAllForRotation(); - - assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse(); - } - - private void simulateBackups(String packageName, int numberOfBackups) { - while (numberOfBackups > 0) { - mTertiaryKeyRotationTracker.recordBackup(packageName); - numberOfBackups--; - } - } - - private static TertiaryKeyRotationTracker newInstance() { - return TertiaryKeyRotationTracker.getInstance(RuntimeEnvironment.application); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java deleted file mode 100644 index 87f21bfa59c2..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.storage; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -/** Tests for {@link BackupEncryptionDb}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class BackupEncryptionDbTest { - private BackupEncryptionDb mBackupEncryptionDb; - - /** Creates an empty {@link BackupEncryptionDb} */ - @Before - public void setUp() { - mBackupEncryptionDb = BackupEncryptionDb.newInstance(RuntimeEnvironment.application); - } - - /** - * Tests that the tertiary keys table gets cleared when calling {@link - * BackupEncryptionDb#clear()}. - */ - @Test - public void clear_withNonEmptyTertiaryKeysTable_clearsTertiaryKeysTable() throws Exception { - String secondaryKeyAlias = "secondaryKeyAlias"; - TertiaryKeysTable tertiaryKeysTable = mBackupEncryptionDb.getTertiaryKeysTable(); - tertiaryKeysTable.addKey(new TertiaryKey(secondaryKeyAlias, "packageName", new byte[0])); - - mBackupEncryptionDb.clear(); - - assertThat(tertiaryKeysTable.getAllKeys(secondaryKeyAlias)).isEmpty(); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java deleted file mode 100644 index 319ec89f445e..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.backup.encryption.storage; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import com.android.server.backup.testing.CryptoTestUtils; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.Map; -import java.util.Optional; - -/** Tests for {@link TertiaryKeysTable}. */ -@RunWith(RobolectricTestRunner.class) -@Presubmit -public class TertiaryKeysTableTest { - private static final int KEY_SIZE_BYTES = 32; - private static final String SECONDARY_ALIAS = "phoebe"; - private static final String PACKAGE_NAME = "generic.package.name"; - - private TertiaryKeysTable mTertiaryKeysTable; - - /** Creates an empty {@link BackupEncryptionDb}. */ - @Before - public void setUp() { - mTertiaryKeysTable = - BackupEncryptionDb.newInstance(RuntimeEnvironment.application) - .getTertiaryKeysTable(); - } - - /** Tests that new {@link TertiaryKey}s get successfully added to the database. */ - @Test - public void addKey_onEmptyDatabase_putsKeyInDb() throws Exception { - byte[] key = generateRandomKey(); - TertiaryKey keyToInsert = new TertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME, key); - - long result = mTertiaryKeysTable.addKey(keyToInsert); - - assertThat(result).isNotEqualTo(-1); - Optional<TertiaryKey> maybeKeyInDb = - mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME); - assertThat(maybeKeyInDb.isPresent()).isTrue(); - TertiaryKey keyInDb = maybeKeyInDb.get(); - assertTertiaryKeysEqual(keyInDb, keyToInsert); - } - - /** Tests that keys replace older keys with the same secondary alias and package name. */ - @Test - public void addKey_havingSameSecondaryAliasAndPackageName_replacesOldKey() throws Exception { - mTertiaryKeysTable.addKey( - new TertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME, generateRandomKey())); - byte[] newKey = generateRandomKey(); - - long result = - mTertiaryKeysTable.addKey(new TertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME, newKey)); - - assertThat(result).isNotEqualTo(-1); - TertiaryKey keyInDb = mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME).get(); - assertThat(keyInDb.getWrappedKeyBytes()).isEqualTo(newKey); - } - - /** - * Tests that keys do not replace older keys with the same package name but a different alias. - */ - @Test - public void addKey_havingSamePackageNameButDifferentAlias_doesNotReplaceOldKey() - throws Exception { - String alias2 = "karl"; - TertiaryKey key1 = generateTertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME); - TertiaryKey key2 = generateTertiaryKey(alias2, PACKAGE_NAME); - - long primaryKey1 = mTertiaryKeysTable.addKey(key1); - long primaryKey2 = mTertiaryKeysTable.addKey(key2); - - assertThat(primaryKey1).isNotEqualTo(primaryKey2); - assertThat(mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME).isPresent()).isTrue(); - assertTertiaryKeysEqual( - mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME).get(), key1); - assertThat(mTertiaryKeysTable.getKey(alias2, PACKAGE_NAME).isPresent()).isTrue(); - assertTertiaryKeysEqual(mTertiaryKeysTable.getKey(alias2, PACKAGE_NAME).get(), key2); - } - - /** - * Tests that {@link TertiaryKeysTable#getKey(String, String)} returns an empty {@link Optional} - * for a missing key. - */ - @Test - public void getKey_forMissingKey_returnsEmptyOptional() throws Exception { - Optional<TertiaryKey> key = mTertiaryKeysTable.getKey(SECONDARY_ALIAS, PACKAGE_NAME); - - assertThat(key.isPresent()).isFalse(); - } - - /** - * Tests that {@link TertiaryKeysTable#getAllKeys(String)} returns an empty map when no keys - * with the secondary alias exist. - */ - @Test - public void getAllKeys_withNoKeysForAlias_returnsEmptyMap() throws Exception { - assertThat(mTertiaryKeysTable.getAllKeys(SECONDARY_ALIAS)).isEmpty(); - } - - /** - * Tests that {@link TertiaryKeysTable#getAllKeys(String)} returns all keys corresponding to the - * provided secondary alias. - */ - @Test - public void getAllKeys_withMatchingKeys_returnsAllKeysWrappedWithSecondary() throws Exception { - TertiaryKey key1 = generateTertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME); - mTertiaryKeysTable.addKey(key1); - String package2 = "generic.package.two"; - TertiaryKey key2 = generateTertiaryKey(SECONDARY_ALIAS, package2); - mTertiaryKeysTable.addKey(key2); - String package3 = "generic.package.three"; - TertiaryKey key3 = generateTertiaryKey(SECONDARY_ALIAS, package3); - mTertiaryKeysTable.addKey(key3); - - Map<String, TertiaryKey> keysByPackageName = mTertiaryKeysTable.getAllKeys(SECONDARY_ALIAS); - - assertThat(keysByPackageName).hasSize(3); - assertThat(keysByPackageName).containsKey(PACKAGE_NAME); - assertTertiaryKeysEqual(keysByPackageName.get(PACKAGE_NAME), key1); - assertThat(keysByPackageName).containsKey(package2); - assertTertiaryKeysEqual(keysByPackageName.get(package2), key2); - assertThat(keysByPackageName).containsKey(package3); - assertTertiaryKeysEqual(keysByPackageName.get(package3), key3); - } - - /** - * Tests that {@link TertiaryKeysTable#getAllKeys(String)} does not return any keys wrapped with - * another alias. - */ - @Test - public void getAllKeys_withMatchingKeys_doesNotReturnKeysWrappedWithOtherAlias() - throws Exception { - mTertiaryKeysTable.addKey(generateTertiaryKey(SECONDARY_ALIAS, PACKAGE_NAME)); - mTertiaryKeysTable.addKey(generateTertiaryKey("somekey", "generic.package.two")); - - Map<String, TertiaryKey> keysByPackageName = mTertiaryKeysTable.getAllKeys(SECONDARY_ALIAS); - - assertThat(keysByPackageName).hasSize(1); - assertThat(keysByPackageName).containsKey(PACKAGE_NAME); - } - - private void assertTertiaryKeysEqual(TertiaryKey a, TertiaryKey b) { - assertThat(a.getSecondaryKeyAlias()).isEqualTo(b.getSecondaryKeyAlias()); - assertThat(a.getPackageName()).isEqualTo(b.getPackageName()); - assertThat(a.getWrappedKeyBytes()).isEqualTo(b.getWrappedKeyBytes()); - } - - private TertiaryKey generateTertiaryKey(String alias, String packageName) { - return new TertiaryKey(alias, packageName, generateRandomKey()); - } - - private byte[] generateRandomKey() { - return CryptoTestUtils.generateRandomBytes(KEY_SIZE_BYTES); - } -} diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java index 44e9e6aecacd..e49425b00322 100644 --- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java +++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java @@ -32,6 +32,7 @@ import com.android.server.backup.TransportManager; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.testing.BackupManagerServiceTestUtils; import com.android.server.testing.shadows.ShadowApplicationPackageManager; +import com.android.server.testing.shadows.ShadowSystemServiceRegistry; import org.junit.Before; import org.junit.Test; @@ -51,7 +52,12 @@ import java.io.File; * UserBackupManagerService}. */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowApplicationPackageManager.class, ShadowJobScheduler.class}) +@Config( + shadows = { + ShadowApplicationPackageManager.class, + ShadowJobScheduler.class, + ShadowSystemServiceRegistry.class + }) @Presubmit public class SetupObserverTest { private static final String TAG = "SetupObserverTest"; diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java index a1b8a9520524..14b4dc3dc7d0 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java @@ -77,4 +77,16 @@ public class KeyValueBackupReporterTest { assertThat(observer).isEqualTo(mObserver); } + + /** + * Ensure that EventLog is called when logging the transport uninitialised issue. + */ + @Test + public void testOnTransportNotInitialized_callsEventLog() { + ShadowEventLog.setUp(); + + mReporter.onTransportNotInitialized("transport"); + + assertThat(ShadowEventLog.getEntries().size()).isEqualTo(1); + } } diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 1d082c735d09..ec56e1ebc8e0 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -120,6 +120,7 @@ import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowBackupDataInput; import com.android.server.testing.shadows.ShadowBackupDataOutput; import com.android.server.testing.shadows.ShadowEventLog; +import com.android.server.testing.shadows.ShadowSystemServiceRegistry; import com.google.common.truth.IterableSubject; @@ -130,6 +131,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -163,10 +165,11 @@ import java.util.stream.Stream; ShadowBackupDataInput.class, ShadowBackupDataOutput.class, ShadowEventLog.class, - ShadowQueuedWork.class + ShadowQueuedWork.class, + ShadowSystemServiceRegistry.class }) @Presubmit -public class KeyValueBackupTaskTest { +public class KeyValueBackupTaskTest { private static final PackageData PACKAGE_1 = keyValuePackage(1); private static final PackageData PACKAGE_2 = keyValuePackage(2); private static final String BACKUP_AGENT_SHARED_PREFS_SYNCHRONIZER_CLASS = @@ -225,8 +228,9 @@ public class KeyValueBackupTaskTest { // Needed to be able to use a real BMS instead of a mock setUpBinderCallerAndApplicationAsSystem(mApplication); mBackupManagerService = - spy(createUserBackupManagerServiceAndRunTasks( - USER_ID, mContext, mBaseStateDir, mDataDir, mTransportManager)); + spy( + createUserBackupManagerServiceAndRunTasks( + USER_ID, mContext, mBaseStateDir, mDataDir, mTransportManager)); setUpBackupManagerServiceBasics( mBackupManagerService, mApplication, @@ -334,9 +338,7 @@ public class KeyValueBackupTaskTest { .isEqualTo("packageState".getBytes()); } - /** - * Do not update backup token if the backup queue was empty - */ + /** Do not update backup token if the backup queue was empty */ @Test public void testRunTask_whenQueueEmptyOnFirstBackup_doesNotUpdateCurrentToken() throws Exception { @@ -712,7 +714,7 @@ public class KeyValueBackupTaskTest { // Verify has set work source and hasn't unset yet. verify(mBackupManagerService) .setWorkSource( - argThat(workSource -> workSource.get(0) == PACKAGE_1.uid)); + argThat(workSource -> workSource.getUid(0) == PACKAGE_1.uid)); verify(mBackupManagerService, never()).setWorkSource(null); }); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); @@ -724,8 +726,8 @@ public class KeyValueBackupTaskTest { } /** - * Agent unavailable means {@link UserBackupManagerService#bindToAgentSynchronous(ApplicationInfo, - * int)} returns {@code null}. + * Agent unavailable means {@link + * UserBackupManagerService#bindToAgentSynchronous(ApplicationInfo, int)} returns {@code null}. * * @see #setUpAgent(PackageData) */ @@ -758,8 +760,8 @@ public class KeyValueBackupTaskTest { runTask(task); - assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1))).isEqualTo( - "newState".getBytes()); + assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1))) + .isEqualTo("newState".getBytes()); } @Test @@ -794,7 +796,8 @@ public class KeyValueBackupTaskTest { } @Test - public void testRunTask_whenNonIncrementalAndBindToAgentThrowsSecurityException() throws Exception { + public void testRunTask_whenNonIncrementalAndBindToAgentThrowsSecurityException() + throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); doThrow(SecurityException.class) @@ -1664,7 +1667,7 @@ public class KeyValueBackupTaskTest { runTask(task); verify(mReporter).onPackageBackupTransportFailure(PACKAGE_1.packageName); - verify(mReporter).onTransportNotInitialized(); + verify(mReporter).onTransportNotInitialized(mTransport.transportName); verify(mReporter).onBackupFinished(BackupManager.ERROR_TRANSPORT_ABORTED); } @@ -1681,7 +1684,7 @@ public class KeyValueBackupTaskTest { runTask(task); verify(mReporter).onPackageBackupTransportFailure(PM_PACKAGE.packageName); - verify(mReporter).onTransportNotInitialized(); + verify(mReporter).onTransportNotInitialized(mTransport.transportName); verify(mReporter).onBackupFinished(BackupManager.ERROR_TRANSPORT_ABORTED); } @@ -1770,9 +1773,10 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); when(transportMock.transport.performBackup(any(), any(), anyInt())) .thenReturn(BackupTransport.TRANSPORT_NOT_INITIALIZED); - // First one is in startTask(), second is the one we want. + // First one is in startTask(), second is in finishTask(), the third is the one we want. when(transportMock.transport.name()) .thenReturn(mTransport.transportName) + .thenReturn(mTransport.transportName) .thenThrow(DeadObjectException.class); setUpAgentWithData(PACKAGE_1); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); @@ -2320,9 +2324,7 @@ public class KeyValueBackupTaskTest { expectThrows(IllegalArgumentException.class, () -> task.handleCancel(false)); } - /** - * Do not update backup token if no data was moved. - */ + /** Do not update backup token if no data was moved. */ @Test public void testRunTask_whenNoDataToBackupOnFirstBackup_doesNotUpdateCurrentToken() throws Exception { @@ -2338,6 +2340,85 @@ public class KeyValueBackupTaskTest { assertThat(mBackupManagerService.getCurrentToken()).isEqualTo(0L); } + /** Do not inform transport of an empty backup if the app hasn't backed up before */ + @Test + public void testRunTask_whenNoDataToBackupOnFirstBackup_doesNotTellTransportOfBackup() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + mBackupManagerService.setCurrentToken(0L); + when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L); + setUpAgent(PACKAGE_1); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1); + + runTask(task); + + verify(transportMock.transport, never()) + .performBackup( + argThat(packageInfo(PACKAGE_1)), any(ParcelFileDescriptor.class), anyInt()); + } + + /** Let the transport know if there are no changes for a KV backed-up package. */ + @Test + public void testRunTask_whenBackupHasCompletedAndThenNoDataChanges_transportGetsNotified() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L); + when(transportMock.transport.isAppEligibleForBackup( + argThat(packageInfo(PACKAGE_1)), eq(false))) + .thenReturn(true); + when(transportMock.transport.isAppEligibleForBackup( + argThat(packageInfo(PACKAGE_2)), eq(false))) + .thenReturn(true); + setUpAgentWithData(PACKAGE_1); + setUpAgentWithData(PACKAGE_2); + + PackageInfo endSentinel = new PackageInfo(); + endSentinel.packageName = KeyValueBackupTask.NO_DATA_END_SENTINEL; + + // Perform First Backup run, which should backup both packages + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2); + runTask(task); + InOrder order = Mockito.inOrder(transportMock.transport); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_1)), + any(), + eq(BackupTransport.FLAG_NON_INCREMENTAL)); + order.verify(transportMock.transport).finishBackup(); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_2)), + any(), + eq(BackupTransport.FLAG_NON_INCREMENTAL)); + order.verify(transportMock.transport).finishBackup(); + + // Run again with new data for package 1, but nothing new for package 2 + task = createKeyValueBackupTask(transportMock, PACKAGE_1); + runTask(task); + + // Now for the second run we performed one incremental backup (package 1) and + // made one "no change" call (package 2) before sending the end sentinel. + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_1)), + any(), + eq(BackupTransport.FLAG_INCREMENTAL)); + order.verify(transportMock.transport).finishBackup(); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_2)), + any(), + eq(BackupTransport.FLAG_DATA_NOT_CHANGED)); + order.verify(transportMock.transport).finishBackup(); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(endSentinel)), + any(), + eq(BackupTransport.FLAG_DATA_NOT_CHANGED)); + order.verify(transportMock.transport).finishBackup(); + order.verifyNoMoreInteractions(); + } + private void runTask(KeyValueBackupTask task) { // Pretend we are not on the main-thread to prevent RemoteCall from complaining mShadowMainLooper.setCurrentThread(false); @@ -2575,6 +2656,20 @@ public class KeyValueBackupTaskTest { packageInfo != null && packageData.packageName.equals(packageInfo.packageName); } + /** Matches {@link PackageInfo} whose package name is {@code packageData.packageName}. */ + private static ArgumentMatcher<PackageInfo> packageInfo(PackageInfo packageData) { + // We have to test for packageInfo nulity because of Mockito's own stubbing with argThat(). + // E.g. if you do: + // + // 1. when(object.method(argThat(str -> str.equals("foo")))).thenReturn(0) + // 2. when(object.method(argThat(str -> str.equals("bar")))).thenReturn(2) + // + // The second line will throw NPE because it will call lambda 1 with null, since argThat() + // returns null. So we guard against that by checking for null. + return packageInfo -> + packageInfo != null && packageInfo.packageName.equals(packageInfo.packageName); + } + /** Matches {@link ApplicationInfo} whose package name is {@code packageData.packageName}. */ private static ArgumentMatcher<ApplicationInfo> applicationInfo(PackageData packageData) { return applicationInfo -> diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java index 392d182328a5..77b5b61b8f01 100644 --- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java +++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java @@ -37,7 +37,7 @@ import android.os.Process; import android.util.Log; import com.android.server.backup.BackupAgentTimeoutParameters; -import com.android.server.backup.Trampoline; +import com.android.server.backup.BackupManagerService; import com.android.server.backup.TransportManager; import com.android.server.backup.UserBackupManagerService; @@ -89,7 +89,7 @@ public class BackupManagerServiceTestUtils { UserBackupManagerService.createAndInitializeService( userId, context, - new Trampoline(context), + new BackupManagerService(context), backupThread, baseStateDir, dataDir, @@ -166,7 +166,7 @@ public class BackupManagerServiceTestUtils { PowerManager powerManager = (PowerManager) application.getSystemService(Context.POWER_SERVICE); return new UserBackupManagerService.BackupWakeLock( - powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*")); + powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"), 0); } /** diff --git a/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java deleted file mode 100644 index 0428796f51fa..000000000000 --- a/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.testing; - -import java.security.NoSuchAlgorithmException; -import java.util.Random; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -/** Helpers for crypto code tests. */ -public class CryptoTestUtils { - private static final String KEY_ALGORITHM = "AES"; - private static final int KEY_SIZE_BITS = 256; - - private CryptoTestUtils() {} - - public static SecretKey generateAesKey() throws NoSuchAlgorithmException { - KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); - keyGenerator.init(KEY_SIZE_BITS); - return keyGenerator.generateKey(); - } - - /** Generates a byte array of size {@code n} containing random bytes. */ - public static byte[] generateRandomBytes(int n) { - byte[] bytes = new byte[n]; - Random random = new Random(); - random.nextBytes(bytes); - return bytes; - } -} diff --git a/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java new file mode 100644 index 000000000000..2d0fe5875301 --- /dev/null +++ b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import com.android.internal.util.IndentingPrintWriter; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Unit tests for {@link LocationRequestStatistics}. + */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class LocationRequestStatisticsTest { + private static final String FEATURE_ID = "featureId"; + + /** + * Check adding and removing requests & strings + */ + @Test + public void testRequestSummary() { + LocationRequestStatistics.RequestSummary summary = + new LocationRequestStatistics.RequestSummary( + "com.example", FEATURE_ID, "gps", 1000); + StringWriter stringWriter = new StringWriter(); + summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriter), " "), 1234); + assertThat(stringWriter.toString()).startsWith("At"); + + StringWriter stringWriterRemove = new StringWriter(); + summary = new LocationRequestStatistics.RequestSummary( + "com.example", "gps", FEATURE_ID, + LocationRequestStatistics.RequestSummary.REQUEST_ENDED_INTERVAL); + summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriterRemove), " "), 2345); + assertThat(stringWriterRemove.toString()).contains("-"); + assertThat(stringWriterRemove.toString()).contains(FEATURE_ID); + } + + /** + * Check summary list size capping + */ + @Test + public void testSummaryList() { + LocationRequestStatistics statistics = new LocationRequestStatistics(); + statistics.history.addRequest("com.example", FEATURE_ID, "gps", 1000); + assertThat(statistics.history.mList.size()).isEqualTo(1); + // Try (not) to overflow + for (int i = 0; i < LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE; i++) { + statistics.history.addRequest("com.example", FEATURE_ID, "gps", 1000); + } + assertThat(statistics.history.mList.size()).isEqualTo( + LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE); + } +} diff --git a/services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java new file mode 100644 index 000000000000..d8acd6ea6948 --- /dev/null +++ b/services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.Handler; +import android.os.Looper; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +/** + * Unit tests for {@link GnssAntennaInfoProvider}. + */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class GnssAntennaInfoProviderTest { + @Mock + private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative + mMockNative; + private GnssAntennaInfoProvider mTestProvider; + + /** Setup. */ + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mMockNative.startAntennaInfoListening()).thenReturn(true); + when(mMockNative.stopAntennaInfoListening()).thenReturn(true); + + mTestProvider = new GnssAntennaInfoProvider(RuntimeEnvironment.application, + new Handler(Looper.myLooper()), mMockNative) { + @Override + public boolean isGpsEnabled() { + return true; + } + }; + } + + /** + * Test that registerWithService calls the native startAntennaInfoListening method. + */ + @Test + public void register_nativeStarted() { + mTestProvider.registerWithService(); + verify(mMockNative, times(1)).startAntennaInfoListening(); + } + + /** + * Test that unregisterFromService calls the native stopAntennaInfoListening method. + */ + @Test + public void unregister_nativeStopped() { + mTestProvider.registerWithService(); + mTestProvider.unregisterFromService(); + verify(mMockNative, times(1)).stopAntennaInfoListening(); + } + + /** + * Test that GnssAntennaInfoProvider.isAntennaInfoSupported() returns the result of the + * native isAntennaInfoSupported method. + */ + @Test + public void isSupported_nativeIsSupported() { + when(mMockNative.isAntennaInfoSupported()).thenReturn(true); + assertThat(mTestProvider.isAvailableInPlatform()).isTrue(); + + when(mMockNative.isAntennaInfoSupported()).thenReturn(false); + assertThat(mTestProvider.isAvailableInPlatform()).isFalse(); + } +} diff --git a/services/robotests/src/com/android/server/location/GnssBatchingProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssBatchingProviderTest.java index d58c3f73b8e8..25d6aa4dae29 100644 --- a/services/robotests/src/com/android/server/location/GnssBatchingProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssBatchingProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -11,7 +27,7 @@ import static org.mockito.Mockito.when; import android.platform.test.annotations.Presubmit; -import com.android.server.location.GnssBatchingProvider.GnssBatchingProviderNative; +import com.android.server.location.gnss.GnssBatchingProvider.GnssBatchingProviderNative; import org.junit.Before; import org.junit.Test; diff --git a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java index 30c73368da15..4a533aac01db 100644 --- a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyInt; diff --git a/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java index b349b67dab0c..e7d9ef810e51 100644 --- a/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -26,7 +42,8 @@ import org.robolectric.RuntimeEnvironment; @Presubmit public class GnssMeasurementsProviderTest { @Mock - private GnssMeasurementsProvider.GnssMeasurementProviderNative mMockNative; + private GnssMeasurementsProvider.GnssMeasurementProviderNative + mMockNative; private GnssMeasurementsProvider mTestProvider; @Before diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssNavigationMessageProviderTest.java index aa2a96e6fad4..c21db73fb56c 100644 --- a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssNavigationMessageProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -25,7 +41,8 @@ import org.robolectric.RuntimeEnvironment; @Presubmit public class GnssNavigationMessageProviderTest { @Mock - private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative mMockNative; + private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative + mMockNative; private GnssNavigationMessageProvider mTestProvider; @Before diff --git a/services/robotests/src/com/android/server/location/GnssPositionModeTest.java b/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java index f37f50e76ae5..7117ff95b401 100644 --- a/services/robotests/src/com/android/server/location/GnssPositionModeTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; import static com.google.common.truth.Truth.assertThat; diff --git a/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java b/services/robotests/src/com/android/server/location/gnss/GnssSatelliteBlacklistHelperTest.java index ba4a753e4813..7c73a2f92f71 100644 --- a/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssSatelliteBlacklistHelperTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -35,7 +51,8 @@ public class GnssSatelliteBlacklistHelperTest { private ContentResolver mContentResolver; @Mock - private GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback mCallback; + private GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback + mCallback; @Before public void setUp() { diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java index 9c5d4ad6ceeb..9b59aaddc4d4 100644 --- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java +++ b/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -10,7 +26,7 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.NtpTrustedTime; -import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; +import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback; import org.junit.Before; import org.junit.Test; diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java new file mode 100644 index 000000000000..4b25890e5fdb --- /dev/null +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -0,0 +1,754 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; +import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; +import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; +import static android.content.Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND; +import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; +import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.Manifest; +import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; +import android.app.AppOpsManager; +import android.app.AppOpsManager.Mode; +import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManagerInternal; +import android.content.ComponentName; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.PermissionInfo; +import android.content.pm.ResolveInfo; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; +import com.android.server.LocalServices; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.PackageImpl; +import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; +import com.android.server.testing.shadows.ShadowUserManager; +import com.android.server.wm.ActivityTaskManagerInternal; + +import com.google.android.collect.Lists; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Unit tests for {@link CrossProfileAppsServiceImpl}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +@Config(shadows = {ShadowUserManager.class, ShadowApplicationPackageManager.class}) +public class CrossProfileAppsServiceImplRoboTest { + private static final int CALLING_UID = 1111; + private static final int CALLING_PID = 1000; + private static final String CROSS_PROFILE_APP_PACKAGE_NAME = + "com.android.server.pm.crossprofileappsserviceimplrobotest.crossprofileapp"; + private static final int PERSONAL_PROFILE_USER_ID = 0; + private static final int PERSONAL_PROFILE_UID = 2222; + private static final int WORK_PROFILE_USER_ID = 10; + private static final int WORK_PROFILE_UID = 3333; + private static final int OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID = 20; + private static final int OUTSIDE_PROFILE_GROUP_USER_ID = 30; + + private final ContextWrapper mContext = ApplicationProvider.getApplicationContext(); + private final UserManager mUserManager = mContext.getSystemService(UserManager.class); + private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + private final PackageManager mPackageManager = mContext.getPackageManager(); + private final TestInjector mInjector = new TestInjector(); + private final CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl = + new CrossProfileAppsServiceImpl(mContext, mInjector); + private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>(); + private final Map<Integer, List<ApplicationInfo>> installedApplications = new HashMap<>(); + private final Set<Integer> mKilledUids = new HashSet<>(); + + @Mock private PackageManagerInternal mPackageManagerInternal; + @Mock private IPackageManager mIPackageManager; + @Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal; + + @Before + public void initializeMocks() throws Exception { + MockitoAnnotations.initMocks(this); + initializeInstalledApplicationsMock(); + mockCrossProfileAppInstalledAndEnabledOnEachProfile(); + mockCrossProfileAppRequestsInteractAcrossProfiles(); + mockCrossProfileAppRegistersBroadcastReceiver(); + mockCrossProfileAppWhitelisted(); + } + + private void initializeInstalledApplicationsMock() { + when(mPackageManagerInternal.getInstalledApplications(anyInt(), anyInt(), eq(CALLING_UID))) + .thenAnswer(invocation -> installedApplications.get(invocation.getArgument(1))); + } + + private void mockCrossProfileAppInstalledAndEnabledOnEachProfile() { + // They are enabled by default, so we simply have to ensure that a package info with an + // application info is returned. + final PackageInfo packageInfo = buildTestPackageInfo(); + mockCrossProfileAppInstalledOnProfile( + packageInfo, PERSONAL_PROFILE_USER_ID, PERSONAL_PROFILE_UID); + mockCrossProfileAppInstalledOnProfile(packageInfo, WORK_PROFILE_USER_ID, WORK_PROFILE_UID); + } + + private void mockCrossProfileAppInstalledOnProfile( + PackageInfo packageInfo, @UserIdInt int userId, int uid) { + when(mPackageManagerInternal.getPackageInfo( + eq(CROSS_PROFILE_APP_PACKAGE_NAME), + /* flags= */ anyInt(), + /* filterCallingUid= */ anyInt(), + eq(userId))) + .thenReturn(packageInfo); + when(mPackageManagerInternal.getPackage(uid)) + .thenReturn(((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME) + .hideAsParsed()).hideAsFinal()); + installedApplications.putIfAbsent(userId, new ArrayList<>()); + installedApplications.get(userId).add(packageInfo.applicationInfo); + } + + private PackageInfo buildTestPackageInfo() { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME; + return packageInfo; + } + + private void mockCrossProfileAppRequestsInteractAcrossProfiles() throws Exception { + final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES; + when(mIPackageManager.getAppOpPermissionPackages(permissionName)) + .thenReturn(new String[] {CROSS_PROFILE_APP_PACKAGE_NAME}); + } + + private void mockCrossProfileAppRegistersBroadcastReceiver() { + final ShadowApplicationPackageManager shadowApplicationPackageManager = + Shadow.extract(mPackageManager); + final Intent baseIntent = + new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) + .setPackage(CROSS_PROFILE_APP_PACKAGE_NAME); + final Intent manifestIntent = + new Intent(baseIntent) + .setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND + | Intent.FLAG_RECEIVER_FOREGROUND); + final Intent registeredIntent = + new Intent(baseIntent).setFlags(FLAG_RECEIVER_REGISTERED_ONLY); + final List<ResolveInfo> resolveInfos = Lists.newArrayList(buildTestResolveInfo()); + shadowApplicationPackageManager.setResolveInfosForIntent(manifestIntent, resolveInfos); + shadowApplicationPackageManager.setResolveInfosForIntent(registeredIntent, resolveInfos); + } + + private ResolveInfo buildTestResolveInfo() { + final ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME; + resolveInfo.activityInfo.name = CROSS_PROFILE_APP_PACKAGE_NAME + ".Receiver"; + return resolveInfo; + } + + private void mockCrossProfileAppWhitelisted() { + when(mDevicePolicyManagerInternal.getAllCrossProfilePackages()) + .thenReturn(Lists.newArrayList(CROSS_PROFILE_APP_PACKAGE_NAME)); + } + + @Before + public void setUpCrossProfileAppUidsAndPackageNames() { + ShadowApplicationPackageManager.setPackageUidAsUser( + CROSS_PROFILE_APP_PACKAGE_NAME, PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID); + ShadowApplicationPackageManager.setPackageUidAsUser( + CROSS_PROFILE_APP_PACKAGE_NAME, WORK_PROFILE_UID, WORK_PROFILE_USER_ID); + when(mPackageManagerInternal.getPackageUidInternal( + CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, PERSONAL_PROFILE_USER_ID)) + .thenReturn(PERSONAL_PROFILE_UID); + when(mPackageManagerInternal.getPackageUidInternal( + CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, WORK_PROFILE_USER_ID)) + .thenReturn(WORK_PROFILE_UID); + } + + @Before + public void grantPermissions() { + grantPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS, + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); + } + + @Before + public void setUpProfiles() { + final ShadowUserManager shadowUserManager = Shadow.extract(mUserManager); + shadowUserManager.addProfileIds( + PERSONAL_PROFILE_USER_ID, + WORK_PROFILE_USER_ID, + OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID); + shadowUserManager.addProfileIds(OUTSIDE_PROFILE_GROUP_USER_ID); + } + + @Before + public void setInteractAcrossProfilesAppOpDefault() { + // It seems to be necessary to provide the shadow with the default already specified in + // AppOpsManager. + final int defaultMode = AppOpsManager.opToDefaultMode(OP_INTERACT_ACROSS_PROFILES); + explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, defaultMode); + explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode); + } + + @Test + public void setInteractAcrossProfilesAppOp_noPermissions_throwsSecurityException() { + denyPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS, + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); + try { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + fail(); + } catch (SecurityException expected) {} + } + + @Test + public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() { + denyPermissions( + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); + grantPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); + try { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + fail(); + } catch (SecurityException expected) {} + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOp() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_configureInteractAcrossProfilesPermissionWithoutAppOpsPermissions_setsAppOp() { + denyPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS); + grantPermissions( + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, + Manifest.permission.INTERACT_ACROSS_USERS); + + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_appOpsPermissionsWithoutConfigureInteractAcrossProfilesPermission_setsAppOp() { + denyPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); + grantPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS, + Manifest.permission.INTERACT_ACROSS_USERS); + + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() { + denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() { + denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); + grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOpOnOtherProfile() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp(WORK_PROFILE_UID)).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_sendsBroadcast() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isTrue(); + } + + @Test + public void setInteractAcrossProfilesAppOp_sendsBroadcastToOtherProfile() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(WORK_PROFILE_USER_ID)) + .isTrue(); + } + + @Test + public void setInteractAcrossProfilesAppOp_doesNotSendBroadcastToProfileWithoutPackage() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast( + OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID)) + .isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_toSameAsCurrent_doesNotSendBroadcast() { + explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSet() { + mockCrossProfileAppNotWhitelisted(); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isNotEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSendBroadcast() { + mockCrossProfileAppNotWhitelisted(); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_withoutCrossProfileAttribute_manifestReceiversDoNotGetBroadcast() { + declareCrossProfileAttributeOnCrossProfileApp(false); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_withCrossProfileAttribute_manifestReceiversGetBroadcast() { + declareCrossProfileAttributeOnCrossProfileApp(true); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isTrue(); + } + + @Test + public void setInteractAcrossProfilesAppOp_toAllowed_doesNotKillApp() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(mKilledUids).isEmpty(); + } + + @Test + public void setInteractAcrossProfilesAppOp_toDisallowed_killsAppsInBothProfiles() { + shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo()); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_DEFAULT); + + assertThat(mKilledUids).contains(WORK_PROFILE_UID); + assertThat(mKilledUids).contains(PERSONAL_PROFILE_UID); + } + + private PermissionInfo createCrossProfilesPermissionInfo() { + PermissionInfo permissionInfo = new PermissionInfo(); + permissionInfo.name = Manifest.permission.INTERACT_ACROSS_PROFILES; + permissionInfo.protectionLevel = PermissionInfo.PROTECTION_FLAG_APPOP; + return permissionInfo; + } + + @Test + public void canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse() { + mockUninstallCrossProfileAppFromWorkProfile(); + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + private void mockUninstallCrossProfileAppFromWorkProfile() { + when(mPackageManagerInternal.getPackageInfo( + eq(CROSS_PROFILE_APP_PACKAGE_NAME), + /* flags= */ anyInt(), + /* filterCallingUid= */ anyInt(), + eq(WORK_PROFILE_USER_ID))) + .thenReturn(null); + when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(null); + } + + @Test + public void canConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse() + throws Exception { + mockCrossProfileAppDoesNotRequestInteractAcrossProfiles(); + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + private void mockCrossProfileAppDoesNotRequestInteractAcrossProfiles() throws Exception { + final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES; + when(mIPackageManager.getAppOpPermissionPackages(permissionName)) + .thenReturn(new String[] {}); + } + + @Test + public void canConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsFalse() { + mockCrossProfileAppNotWhitelisted(); + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canConfigureInteractAcrossProfiles_returnsTrue() { + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() { + mockUninstallCrossProfileAppFromWorkProfile(); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse() + throws Exception { + mockCrossProfileAppDoesNotRequestInteractAcrossProfiles(); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() { + mockCrossProfileAppNotWhitelisted(); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_platformSignedAppWithAutomaticPermission_returnsFalse() { + mockCrossProfileAppNotWhitelistedByOem(); + shadowOf(mContext).grantPermissions( + Process.myPid(), + PERSONAL_PROFILE_UID, + Manifest.permission.INTERACT_ACROSS_PROFILES); + + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerWorkProfile_returnsFalse() { + when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(WORK_PROFILE_USER_ID)) + .thenReturn(buildCrossProfileComponentName()); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOtherProfile_returnsFalse() { + // Normally, the DPC would not be a profile owner of the personal profile, but for the + // purposes of this test, it is just a profile owner of any profile within the profile + // group. + when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(PERSONAL_PROFILE_USER_ID)) + .thenReturn(buildCrossProfileComponentName()); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOutsideProfileGroup_returnsTrue() { + when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(OUTSIDE_PROFILE_GROUP_USER_ID)) + .thenReturn(buildCrossProfileComponentName()); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() { + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void clearInteractAcrossProfilesAppOps() { + explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED); + mCrossProfileAppsServiceImpl.clearInteractAcrossProfilesAppOps(); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT); + } + + private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { + explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); + } + + private void explicitlySetInteractAcrossProfilesAppOp(int uid, @Mode int mode) { + shadowOf(mAppOpsManager).setMode( + OP_INTERACT_ACROSS_PROFILES, uid, CROSS_PROFILE_APP_PACKAGE_NAME, mode); + } + + private void grantPermissions(String... permissions) { + shadowOf(mContext).grantPermissions(Process.myPid(), CALLING_UID, permissions); + } + + private void denyPermissions(String... permissions) { + shadowOf(mContext).denyPermissions(Process.myPid(), CALLING_UID, permissions); + } + + private @Mode int getCrossProfileAppOp() { + return getCrossProfileAppOp(PERSONAL_PROFILE_UID); + } + + private @Mode int getCrossProfileAppOp(int uid) { + return mAppOpsManager.unsafeCheckOpNoThrow( + AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES), + uid, + CROSS_PROFILE_APP_PACKAGE_NAME); + } + + private boolean receivedCanInteractAcrossProfilesChangedBroadcast() { + return receivedCanInteractAcrossProfilesChangedBroadcast(PERSONAL_PROFILE_USER_ID); + } + + private boolean receivedCanInteractAcrossProfilesChangedBroadcast(@UserIdInt int userId) { + final UserHandle userHandle = UserHandle.of(userId); + if (!mSentUserBroadcasts.containsKey(userHandle)) { + return false; + } + return mSentUserBroadcasts.get(userHandle) + .stream() + .anyMatch(this::isBroadcastCanInteractAcrossProfilesChanged); + } + + private boolean isBroadcastCanInteractAcrossProfilesChanged(Intent intent) { + return intent.getAction().equals(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) + && CROSS_PROFILE_APP_PACKAGE_NAME.equals(intent.getPackage()); + } + + private void mockCrossProfileAndroidPackage(AndroidPackage androidPackage) { + when(mPackageManagerInternal.getPackage(CROSS_PROFILE_APP_PACKAGE_NAME)) + .thenReturn(androidPackage); + when(mPackageManagerInternal.getPackage(PERSONAL_PROFILE_UID)).thenReturn(androidPackage); + when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(androidPackage); + } + + private void mockCrossProfileAppNotWhitelisted() { + when(mDevicePolicyManagerInternal.getAllCrossProfilePackages()) + .thenReturn(new ArrayList<>()); + } + + private void mockCrossProfileAppNotWhitelistedByOem() { + when(mDevicePolicyManagerInternal.getDefaultCrossProfilePackages()) + .thenReturn(new ArrayList<>()); + } + + private boolean receivedManifestCanInteractAcrossProfilesChangedBroadcast() { + final UserHandle userHandle = UserHandle.of(PERSONAL_PROFILE_USER_ID); + if (!mSentUserBroadcasts.containsKey(userHandle)) { + return false; + } + return mSentUserBroadcasts.get(userHandle) + .stream() + .anyMatch(this::isBroadcastManifestCanInteractAcrossProfilesChanged); + } + + private boolean isBroadcastManifestCanInteractAcrossProfilesChanged(Intent intent) { + return isBroadcastCanInteractAcrossProfilesChanged(intent) + && (intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) == 0 + && (intent.getFlags() & FLAG_RECEIVER_INCLUDE_BACKGROUND) != 0 + && (intent.getFlags() & FLAG_RECEIVER_FOREGROUND) != 0 + && intent.getComponent() != null + && intent.getComponent().getPackageName().equals(CROSS_PROFILE_APP_PACKAGE_NAME); + } + + private void declareCrossProfileAttributeOnCrossProfileApp(boolean value) { + mockCrossProfileAndroidPackage( + ((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME) + .setCrossProfile(value) + .hideAsParsed()).hideAsFinal()); + } + + private ComponentName buildCrossProfileComponentName() { + return new ComponentName(CROSS_PROFILE_APP_PACKAGE_NAME, "testClassName"); + } + + private class TestInjector implements CrossProfileAppsServiceImpl.Injector { + + @Override + public int getCallingUid() { + return CALLING_UID; + } + + @Override + public int getCallingPid() { + return CALLING_PID; + } + + @Override + public @UserIdInt int getCallingUserId() { + return PERSONAL_PROFILE_USER_ID; + } + + @Override + public UserHandle getCallingUserHandle() { + return UserHandle.of(getCallingUserId()); + } + + @Override + public long clearCallingIdentity() { + return 0; + } + + @Override + public void restoreCallingIdentity(long token) {} + + @Override + public void withCleanCallingIdentity(ThrowingRunnable action) { + action.run(); + } + + @Override + public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { + return action.get(); + } + + @Override + public UserManager getUserManager() { + return mUserManager; + } + + @Override + public PackageManagerInternal getPackageManagerInternal() { + return mPackageManagerInternal; + } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + + @Override + public AppOpsManager getAppOpsManager() { + return mAppOpsManager; + } + + @Override + public ActivityManagerInternal getActivityManagerInternal() { + return LocalServices.getService(ActivityManagerInternal.class); + } + + @Override + public ActivityTaskManagerInternal getActivityTaskManagerInternal() { + return LocalServices.getService(ActivityTaskManagerInternal.class); + } + + @Override + public IPackageManager getIPackageManager() { + return mIPackageManager; + } + + @Override + public DevicePolicyManagerInternal getDevicePolicyManagerInternal() { + return mDevicePolicyManagerInternal; + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user) { + // Robolectric's shadows do not currently support sendBroadcastAsUser. + final Set<Intent> broadcasts = + mSentUserBroadcasts.containsKey(user) + ? mSentUserBroadcasts.get(user) + : new HashSet<>(); + broadcasts.add(intent); + mSentUserBroadcasts.put(user, broadcasts); + mContext.sendBroadcastAsUser(intent, user); + } + + @Override + public int checkComponentPermission( + String permission, int uid, int owningUid, boolean exported) { + // ActivityManager#checkComponentPermission calls through to + // AppGlobals.getPackageManager()#checkUidPermission, which calls through to + // ShadowActivityThread with Robolectric. This method is currently not supported there. + return mContext.checkPermission(permission, Process.myPid(), uid); + } + + @Override + public void killUid(int uid) { + mKilledUids.add(uid); + } + } +} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java index ab121eddff06..aea36e555ad7 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java @@ -19,13 +19,17 @@ package com.android.server.testing.shadows; import static android.content.pm.PackageManager.NameNotFoundException; import android.app.ApplicationPackageManager; +import android.content.Intent; import android.content.pm.PackageInfo; +import android.content.pm.ResolveInfo; +import android.os.UserHandle; import android.util.ArrayMap; import org.robolectric.annotation.Implements; import org.robolectric.annotation.Resetter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,6 +43,7 @@ public class ShadowApplicationPackageManager private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>(); private static final List<PackageInfo> sInstalledPackages = new ArrayList<>(); private static final Map<String, Integer> sPackageUids = new ArrayMap<>(); + private static final Map<Integer, Map<String, Integer>> sUserPackageUids = new ArrayMap<>(); /** * Registers the package {@code packageName} to be returned when invoking {@link @@ -58,6 +63,19 @@ public class ShadowApplicationPackageManager sPackageUids.put(packageName, packageUid); } + /** + * Sets the package uid {@code packageUid} for the package {@code packageName} to be returned + * when invoking {@link ApplicationPackageManager#getPackageUidAsUser(String, int, int)}. + */ + public static void setPackageUidAsUser(String packageName, int packageUid, int userId) { + final Map<String, Integer> userPackageUids = + sUserPackageUids.containsKey(userId) + ? sUserPackageUids.get(userId) + : new HashMap<>(); + userPackageUids.put(packageName, packageUid); + sUserPackageUids.put(userId, userPackageUids); + } + @Override protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) throws NameNotFoundException { @@ -75,12 +93,23 @@ public class ShadowApplicationPackageManager @Override protected int getPackageUidAsUser(String packageName, int flags, int userId) throws NameNotFoundException { + if (sUserPackageUids.containsKey(userId) + && sUserPackageUids.get(userId).containsKey(packageName)) { + return sUserPackageUids.get(userId).get(packageName); + } if (!sPackageUids.containsKey(packageName)) { throw new NameNotFoundException(packageName); } return sPackageUids.get(packageName); } + @Override + protected List<ResolveInfo> queryBroadcastReceiversAsUser( + Intent intent, int flags, UserHandle userHandle) { + // Currently does not handle multi-user. + return queryBroadcastReceivers(intent, flags); + } + /** Clear package state. */ @Resetter public static void reset() { diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java b/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java index c9984bf07059..4055dfc08f90 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowCloseGuard.java @@ -44,5 +44,10 @@ public class ShadowCloseGuard { public void report(String message, Throwable allocationSite) { mReports += 1; } + + @Override + public void report(String message) { + mReports += 1; + } } } diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowEnvironment.java b/services/robotests/src/com/android/server/testing/shadows/ShadowEnvironment.java new file mode 100644 index 000000000000..577b0824e775 --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowEnvironment.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testing.shadows; + +import android.annotation.Nullable; +import android.os.Environment; + +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; + +import java.io.File; +import java.nio.file.Path; + +/** Implementation mimics {@link org.robolectric.shadows.ShadowEnvironment}. */ +@Implements(Environment.class) +public class ShadowEnvironment extends org.robolectric.shadows.ShadowEnvironment { + @Nullable private static Path sDataDirectory; + + /** @see Environment#getDataDirectory() */ + @Implementation + public static File getDataDirectory() { + if (sDataDirectory == null) { + sDataDirectory = RuntimeEnvironment.getTempDirectory().create("data"); + } + return sDataDirectory.toFile(); + } + + /** Resets static state. */ + @Resetter + public static void reset() { + org.robolectric.shadows.ShadowEnvironment.reset(); + sDataDirectory = null; + } +} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java deleted file mode 100644 index 9c06d81ce550..000000000000 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.testing.shadows; - -import android.security.keystore.recovery.InternalRecoveryServiceException; - -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; - -/** Shadow {@link InternalRecoveryServiceException}. */ -@Implements(InternalRecoveryServiceException.class) -public class ShadowInternalRecoveryServiceException { - private String mMessage; - - @Implementation - public void __constructor__(String message) { - mMessage = message; - } - - @Implementation - public void __constructor__(String message, Throwable cause) { - mMessage = message; - } - - @Implementation - public String getMessage() { - return mMessage; - } -} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java index 2cebbebfbaf4..8daef5fad032 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java @@ -29,6 +29,9 @@ import com.android.server.backup.transport.TransportClient; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import java.util.Map; +import java.util.Set; + @Implements(PerformUnifiedRestoreTask.class) public class ShadowPerformUnifiedRestoreTask { @Nullable private static ShadowPerformUnifiedRestoreTask sLastShadow; diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java deleted file mode 100644 index 7dad8a4e3ff3..000000000000 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.testing.shadows; - -import android.content.Context; -import android.security.keystore.recovery.InternalRecoveryServiceException; -import android.security.keystore.recovery.LockScreenRequiredException; -import android.security.keystore.recovery.RecoveryController; - -import com.google.common.collect.ImmutableList; - -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.annotation.Resetter; - -import java.lang.reflect.Constructor; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.util.HashMap; -import java.util.List; - -import javax.crypto.KeyGenerator; - -/** - * Shadow of {@link RecoveryController}. - * - * <p>Instead of generating keys via the {@link RecoveryController}, this shadow generates them in - * memory. - */ -@Implements(RecoveryController.class) -public class ShadowRecoveryController { - private static final String KEY_GENERATOR_ALGORITHM = "AES"; - private static final int KEY_SIZE_BITS = 256; - - private static boolean sIsSupported = true; - private static boolean sThrowsInternalError = false; - private static HashMap<String, Key> sKeysByAlias = new HashMap<>(); - private static HashMap<String, Integer> sKeyStatusesByAlias = new HashMap<>(); - - @Implementation - public void __constructor__() { - // do not throw - } - - @Implementation - public static RecoveryController getInstance(Context context) { - // Call non-public constructor. - try { - Constructor<RecoveryController> constructor = RecoveryController.class.getConstructor(); - return constructor.newInstance(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Implementation - public static boolean isRecoverableKeyStoreEnabled(Context context) { - return sIsSupported; - } - - @Implementation - public Key generateKey(String alias) - throws InternalRecoveryServiceException, LockScreenRequiredException { - maybeThrowError(); - KeyGenerator keyGenerator; - try { - keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM); - } catch (NoSuchAlgorithmException e) { - // Should never happen - throw new RuntimeException(e); - } - - keyGenerator.init(KEY_SIZE_BITS); - Key key = keyGenerator.generateKey(); - sKeysByAlias.put(alias, key); - sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); - return key; - } - - @Implementation - public Key getKey(String alias) - throws InternalRecoveryServiceException, UnrecoverableKeyException { - return sKeysByAlias.get(alias); - } - - @Implementation - public void removeKey(String alias) throws InternalRecoveryServiceException { - sKeyStatusesByAlias.remove(alias); - sKeysByAlias.remove(alias); - } - - @Implementation - public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException { - maybeThrowError(); - return sKeyStatusesByAlias.getOrDefault( - alias, RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE); - } - - @Implementation - public List<String> getAliases() throws InternalRecoveryServiceException { - return ImmutableList.copyOf(sKeyStatusesByAlias.keySet()); - } - - private static void maybeThrowError() throws InternalRecoveryServiceException { - if (sThrowsInternalError) { - throw new InternalRecoveryServiceException("test error"); - } - } - - /** Sets the recovery status of the key with {@code alias} to {@code status}. */ - public static void setRecoveryStatus(String alias, int status) { - sKeyStatusesByAlias.put(alias, status); - } - - /** Sets all existing keys to being synced. */ - public static void syncAllKeys() { - for (String alias : sKeysByAlias.keySet()) { - sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNCED); - } - } - - public static void setThrowsInternalError(boolean throwsInternalError) { - ShadowRecoveryController.sThrowsInternalError = throwsInternalError; - } - - public static void setIsSupported(boolean isSupported) { - ShadowRecoveryController.sIsSupported = isSupported; - } - - @Resetter - public static void reset() { - sIsSupported = true; - sThrowsInternalError = false; - sKeysByAlias.clear(); - sKeyStatusesByAlias.clear(); - } -} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowSystemServiceRegistry.java b/services/robotests/src/com/android/server/testing/shadows/ShadowSystemServiceRegistry.java new file mode 100644 index 000000000000..c59798fc92fc --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowSystemServiceRegistry.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testing.shadows; + +import android.app.SystemServiceRegistry; +import android.app.job.JobSchedulerFrameworkInitializer; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.util.ReflectionHelpers; + +/** + * Shadow of {@link SystemServiceRegistry} + * + * <p>JobSchedulerFrameworkInitializer contains a static initializer registering JobScheduler as a + * system service. We need to make sure the initializer is run before the tests that use + * JobScheduler. And we're putting this on the static initializer of SystemServiceRegistry since + * other services are registered here. + */ +@Implements(className = "android.app.SystemServiceRegistry") +public class ShadowSystemServiceRegistry { + @Implementation + protected static void __staticInitializer__() { + // Make sure the static init in the real class is still executed. + ReflectionHelpers.callStaticMethod(SystemServiceRegistry.class, "__staticInitializer__"); + try { + Class.forName(JobSchedulerFrameworkInitializer.class.getCanonicalName()); + } catch (ClassNotFoundException e) { + // Rethrowing as an unchecked exception because checked exceptions are not allowed in + // static blocks. + throw new ExceptionInInitializerError(e); + } + } +} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java new file mode 100644 index 000000000000..a9e4ee521f90 --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testing.shadows; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.os.UserManager; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** Shadow for {@link UserManager}. */ +@Implements(UserManager.class) +public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { + private final Map<Integer, Set<Integer>> profileIds = new HashMap<>(); + + /** @see UserManager#isUserUnlocked() */ + @Implementation + public boolean isUserUnlocked(@UserIdInt int userId) { + return false; + } + + /** @see UserManager#getProfileIds(int, boolean) () */ + @Implementation + @NonNull + public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) { + // Currently, enabledOnly is ignored. + if (!profileIds.containsKey(userId)) { + return new int[] {userId}; + } + return profileIds.get(userId).stream().mapToInt(Number::intValue).toArray(); + } + + /** Add a collection of profile IDs, all within the same profile group. */ + public void addProfileIds(@UserIdInt int... userIds) { + final Set<Integer> profileGroup = new HashSet<>(); + for (int userId : userIds) { + profileGroup.add(userId); + profileIds.put(userId, profileGroup); + } + } +} |