diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 53f5fe10d3e97..8c09dcde58d0c 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -156,6 +156,16 @@ public class BackupManagerService extends IBackupManager.Stub { } }; + /** + * The user that the backup is activated by default for. + * + * If there is a {@link UserManager#getMainUser()}, this will be that user. If not, it will be + * {@link UserHandle#USER_SYSTEM}. + * + * @see #isBackupActivatedForUser(int) + */ + @UserIdInt private final int mDefaultBackupUserId; + public BackupManagerService(Context context) { this(context, new SparseArray<>()); } @@ -175,6 +185,8 @@ public class BackupManagerService extends IBackupManager.Stub { mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist; mContext.registerReceiver( mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED)); + UserHandle mainUser = getUserManager().getMainUser(); + mDefaultBackupUserId = mainUser == null ? UserHandle.USER_SYSTEM : mainUser.getIdentifier(); } // TODO: Remove this when we implement DI by injecting in the construtor. @@ -183,31 +195,35 @@ public class BackupManagerService extends IBackupManager.Stub { return mHandler; } + @VisibleForTesting protected boolean isBackupDisabled() { return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false); } + @VisibleForTesting protected int binderGetCallingUserId() { return Binder.getCallingUserHandle().getIdentifier(); } + @VisibleForTesting protected int binderGetCallingUid() { return Binder.getCallingUid(); } - /** Stored in the system user's directory. */ - protected File getSuppressFileForSystemUser() { - return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM), - BACKUP_SUPPRESS_FILENAME); + @VisibleForTesting + protected File getSuppressFileForUser(@UserIdInt int userId) { + return new File(UserBackupManagerFiles.getBaseStateDir(userId), BACKUP_SUPPRESS_FILENAME); } /** Stored in the system user's directory and the file is indexed by the user it refers to. */ + @VisibleForTesting protected File getRememberActivatedFileForNonSystemUser(int userId) { return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId); } /** Stored in the system user's directory and the file is indexed by the user it refers to. */ - protected File getActivatedFileForNonSystemUser(int userId) { + @VisibleForTesting + protected File getActivatedFileForUser(int userId) { return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId); } @@ -249,31 +265,40 @@ public class BackupManagerService extends IBackupManager.Stub { } /** - * Deactivates the backup service for user {@code userId}. If this is the system user, it - * creates a suppress file which disables backup for all users. If this is a non-system user, it - * only deactivates backup for that user by deleting its activate file. + * Deactivates the backup service for user {@code userId}. + * + * If this is the system user or the {@link #mDefaultBackupUserId} user, it creates a "suppress" + * file for this user. + * + * Otherwise, it deleties the user's "activated" file. + * + * Note that if backup is deactivated for the system user, it will be deactivated for ALL + * users until it is reactivated for the system user, at which point the per-user activation + * status will be the source of truth. */ @GuardedBy("mStateLock") private void deactivateBackupForUserLocked(int userId) throws IOException { - if (userId == UserHandle.USER_SYSTEM) { - createFile(getSuppressFileForSystemUser()); + if (userId == UserHandle.USER_SYSTEM || userId == mDefaultBackupUserId) { + createFile(getSuppressFileForUser(userId)); } else { - deleteFile(getActivatedFileForNonSystemUser(userId)); + deleteFile(getActivatedFileForUser(userId)); } } /** - * Enables the backup service for user {@code userId}. If this is the system user, it deletes - * the suppress file. If this is a non-system user, it creates the user's activate file. Note, - * deleting the suppress file does not automatically enable backup for non-system users, they - * need their own activate file in order to participate in the service. + * Activates the backup service for user {@code userId}. + * + * If this is the system user or the {@link #mDefaultBackupUserId} user, it deletes its + * "suppress" file. + * + * Otherwise, it creates the user's "activated" file. */ @GuardedBy("mStateLock") private void activateBackupForUserLocked(int userId) throws IOException { - if (userId == UserHandle.USER_SYSTEM) { - deleteFile(getSuppressFileForSystemUser()); + if (userId == UserHandle.USER_SYSTEM || userId == mDefaultBackupUserId) { + deleteFile(getSuppressFileForUser(userId)); } else { - createFile(getActivatedFileForNonSystemUser(userId)); + createFile(getActivatedFileForUser(userId)); } } @@ -286,32 +311,52 @@ public class BackupManagerService extends IBackupManager.Stub { @Override public boolean isUserReadyForBackup(int userId) { enforceCallingPermissionOnUserId(userId, "isUserReadyForBackup()"); - return mUserServices.get(UserHandle.USER_SYSTEM) != null - && mUserServices.get(userId) != null; + return mUserServices.get(userId) != null; } /** - * Backup is activated for the system user if the suppress file does not exist. Backup is - * activated for non-system users if the suppress file does not exist AND the user's activated - * file exists. + * If there is a "suppress" file for the system user, backup is inactive for ALL users. + * + * Otherwise, the below logic applies: + * + * For the {@link #mDefaultBackupUserId}, backup is active by default. Backup is only + * deactivated if there exists a "suppress" file for the user, which can be created by calling + * {@link #setBackupServiceActive}. + * + * For non-main users, backup is only active if there exists an "activated" file for the user, + * which can also be created by calling {@link #setBackupServiceActive}. */ private boolean isBackupActivatedForUser(int userId) { - if (getSuppressFileForSystemUser().exists()) { + if (getSuppressFileForUser(UserHandle.USER_SYSTEM).exists()) { return false; } - return userId == UserHandle.USER_SYSTEM - || getActivatedFileForNonSystemUser(userId).exists(); + boolean isDefaultUser = userId == mDefaultBackupUserId; + + // If the default user is not the system user, we are in headless mode and the system user + // doesn't have an actual human user associated with it. + if ((userId == UserHandle.USER_SYSTEM) && !isDefaultUser) { + return false; + } + + if (isDefaultUser && getSuppressFileForUser(userId).exists()) { + return false; + } + + return isDefaultUser || getActivatedFileForUser(userId).exists(); } + @VisibleForTesting protected Context getContext() { return mContext; } + @VisibleForTesting protected UserManager getUserManager() { return mUserManager; } + @VisibleForTesting protected void postToHandler(Runnable runnable) { mHandler.post(runnable); } @@ -322,6 +367,7 @@ public class BackupManagerService extends IBackupManager.Stub { * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not * essential for device functioning. */ + @VisibleForTesting void onUnlockUser(int userId) { postToHandler(() -> startServiceForUser(userId)); } @@ -357,6 +403,7 @@ public class BackupManagerService extends IBackupManager.Stub { * Starts the backup service for user {@code userId} by registering its instance of {@link * UserBackupManagerService} with this service and setting enabled state. */ + @VisibleForTesting void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) { mUserServices.put(userId, userBackupManagerService); diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java similarity index 53% rename from services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java rename to services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java index 81e06649ebc40..cd9d8a9d023fb 100644 --- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -16,22 +16,28 @@ package com.android.server.backup; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; + import android.Manifest; import android.annotation.UserIdInt; import android.app.backup.BackupManager; @@ -42,6 +48,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.ConditionVariable; +import android.os.FileUtils; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -50,9 +57,10 @@ import android.platform.test.annotations.Presubmit; import android.util.SparseArray; import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.server.backup.utils.RandomAccessFileUtils; import org.junit.After; @@ -61,11 +69,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; import java.io.File; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -76,13 +84,12 @@ import java.util.concurrent.TimeUnit; public class BackupManagerServiceTest { private static final ComponentName TRANSPORT_COMPONENT_NAME = new ComponentName("package", "class"); - private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1; - private static final int UNSTARTED_NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 2; + private static final int NON_SYSTEM_USER = UserHandle.USER_SYSTEM + 1; @UserIdInt private int mUserId; @Mock - private UserBackupManagerService mUserBackupManagerService; + private UserBackupManagerService mSystemUserBackupManagerService; @Mock private UserBackupManagerService mNonSystemUserBackupManagerService; @Mock @@ -97,200 +104,177 @@ public class BackupManagerServiceTest { private FileDescriptor mFileDescriptorStub = new FileDescriptor(); private BackupManagerServiceTestable mService; - private File mTestDir; - private File mSuppressFile; + private static File sTestDir; private SparseArray mUserServices; + private MockitoSession mSession; @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); + mSession = + ExtendedMockito.mockitoSession().initMocks( + this) + .strictness(Strictness.LENIENT) + .spyStatic(UserBackupManagerService.class) + .startMocking(); + doReturn(mSystemUserBackupManagerService).when( + () -> UserBackupManagerService.createAndInitializeService( + eq(UserHandle.USER_SYSTEM), any(), any(), any())); + doReturn(mNonSystemUserBackupManagerService).when( + () -> UserBackupManagerService.createAndInitializeService(eq(NON_SYSTEM_USER), + any(), any(), any())); + mUserId = UserHandle.USER_SYSTEM; mUserServices = new SparseArray<>(); - mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService); - mUserServices.append(NON_USER_SYSTEM, mNonSystemUserBackupManagerService); when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock); - when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock); - when(mUserManagerMock.getUserInfo(UNSTARTED_NON_USER_SYSTEM)).thenReturn(mUserInfoMock); + when(mUserManagerMock.getUserInfo(NON_SYSTEM_USER)).thenReturn(mUserInfoMock); + // Null main user means there is no main user on the device. + when(mUserManagerMock.getMainUser()).thenReturn(null); BackupManagerServiceTestable.sCallingUserId = UserHandle.USER_SYSTEM; BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID; BackupManagerServiceTestable.sBackupDisabled = false; BackupManagerServiceTestable.sUserManagerMock = mUserManagerMock; - mTestDir = InstrumentationRegistry.getContext().getFilesDir(); - mTestDir.mkdirs(); - - mSuppressFile = new File(mTestDir, "suppress"); - BackupManagerServiceTestable.sSuppressFile = mSuppressFile; - - setUpStateFilesForNonSystemUser(NON_USER_SYSTEM); - setUpStateFilesForNonSystemUser(UNSTARTED_NON_USER_SYSTEM); + sTestDir = InstrumentationRegistry.getContext().getFilesDir(); + sTestDir.mkdirs(); when(mContextMock.getSystemService(Context.JOB_SCHEDULER_SERVICE)) .thenReturn(mock(JobScheduler.class)); mService = new BackupManagerServiceTestable(mContextMock, mUserServices); - } - - private void setUpStateFilesForNonSystemUser(int userId) { - File activatedFile = new File(mTestDir, "activate-" + userId); - BackupManagerServiceTestable.sActivatedFiles.append(userId, activatedFile); - File rememberActivatedFile = new File(mTestDir, "rem-activate-" + userId); - BackupManagerServiceTestable.sRememberActivatedFiles.append(userId, rememberActivatedFile); + simulateUserUnlocked(UserHandle.USER_SYSTEM); } @After public void tearDown() throws Exception { - mSuppressFile.delete(); - deleteFiles(BackupManagerServiceTestable.sActivatedFiles); - deleteFiles(BackupManagerServiceTestable.sRememberActivatedFiles); - } - - private void deleteFiles(SparseArray files) { - int numFiles = files.size(); - for (int i = 0; i < numFiles; i++) { - files.valueAt(i).delete(); - } + FileUtils.deleteContentsAndDir(sTestDir); + mSession.finishMocking(); } @Test - public void testIsBackupServiceActive_whenBackupsNotDisabledAndSuppressFileDoesNotExist() { - assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + public void onUnlockUser_startsUserService() { + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + ConditionVariable unlocked = new ConditionVariable(false); + + mService.onUnlockUser(NON_SYSTEM_USER); + mService.getBackupHandler().post(unlocked::open); + unlocked.block(); + + assertNotNull(mService.getUserService(NON_SYSTEM_USER)); } @Test - public void testOnUnlockUser_forNonSystemUserWhenBackupsDisabled_doesNotStartUser() { + public void startServiceForUser_backupDisabledGlobally_doesNotStartUserService() { BackupManagerServiceTestable.sBackupDisabled = true; BackupManagerServiceTestable service = new BackupManagerServiceTestable(mContextMock, new SparseArray<>()); - ConditionVariable unlocked = new ConditionVariable(false); - service.onUnlockUser(NON_USER_SYSTEM); + service.startServiceForUser(UserHandle.USER_SYSTEM); - service.getBackupHandler().post(unlocked::open); - unlocked.block(); - assertNull(service.getUserService(NON_USER_SYSTEM)); - } - - @Test - public void testOnUnlockUser_forSystemUserWhenBackupsDisabled_doesNotStartUser() { - BackupManagerServiceTestable.sBackupDisabled = true; - BackupManagerServiceTestable service = - new BackupManagerServiceTestable(mContextMock, new SparseArray<>()); - ConditionVariable unlocked = new ConditionVariable(false); - - service.onUnlockUser(UserHandle.USER_SYSTEM); - - service.getBackupHandler().post(unlocked::open); - unlocked.block(); assertNull(service.getUserService(UserHandle.USER_SYSTEM)); } @Test - public void testOnUnlockUser_whenBackupNotActivated_doesNotStartUser() { - BackupManagerServiceTestable.sBackupDisabled = false; - BackupManagerServiceTestable service = - new BackupManagerServiceTestable(mContextMock, new SparseArray<>()); - service.setBackupServiceActive(NON_USER_SYSTEM, false); - ConditionVariable unlocked = new ConditionVariable(false); + public void startServiceForUser_backupNotActiveForUser_doesNotStartUserService() { + mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); - service.onUnlockUser(NON_USER_SYSTEM); + mService.startServiceForUser(UserHandle.USER_SYSTEM); - service.getBackupHandler().post(unlocked::open); - unlocked.block(); - assertNull(service.getUserService(NON_USER_SYSTEM)); + assertNull(mService.getUserService(UserHandle.USER_SYSTEM)); } @Test - public void testIsBackupServiceActive_forSystemUserWhenBackupDisabled_returnsTrue() - throws Exception { + public void startServiceForUser_backupEnabledGloballyAndActiveForUser_startsUserService() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + mService.startServiceForUser(NON_SYSTEM_USER); + + assertNotNull(mService.getUserService(NON_SYSTEM_USER)); + } + + @Test + public void isBackupServiceActive_backupDisabledGlobally_returnFalse() { BackupManagerServiceTestable.sBackupDisabled = true; - BackupManagerService backupManagerService = + BackupManagerService service = new BackupManagerServiceTestable(mContextMock, mUserServices); - backupManagerService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); + service.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - assertFalse(backupManagerService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + assertFalse(service.isBackupServiceActive(UserHandle.USER_SYSTEM)); } @Test - public void testIsBackupServiceActive_forNonSystemUserWhenBackupDisabled_returnsTrue() - throws Exception { - BackupManagerServiceTestable.sBackupDisabled = true; - BackupManagerService backupManagerService = - new BackupManagerServiceTestable(mContextMock, mUserServices); - backupManagerService.setBackupServiceActive(NON_USER_SYSTEM, true); - - assertFalse(backupManagerService.isBackupServiceActive(NON_USER_SYSTEM)); - } - - @Test - public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception { - mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - - assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); - } - - @Test - public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception { + public void isBackupServiceActive_systemUser_isDefault_deactivated_returnsFalse() { + // If there's no 'main' user on the device, the default user is the system user. mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); } @Test - public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated() - throws Exception { + public void isBackupServiceActive_systemUser_isNotDefault_returnsFalse() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + + assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + } + + @Test + public void isBackupServiceActive_systemUser_isDefault_returnsTrue() { + // If there's no 'main' user on the device, the default user is the system user. + assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + } + + @Test + public void isBackupServiceActive_nonSystemUser_isDefault_systemUserDeactivated_returnsFalse() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); - mService.setBackupServiceActive(NON_USER_SYSTEM, true); - assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM)); + assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER)); } @Test - public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated() - throws Exception { - mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - // Don't activate non-system user. + public void isBackupServiceActive_nonSystemUser_isDefault_deactivated_returnsFalse() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + mService.setBackupServiceActive(NON_SYSTEM_USER, false); - assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM)); + assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER)); } @Test - public void - isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated() - throws Exception { - mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + public void isBackupServiceActive_nonSystemUser_isDefault_returnsTrue() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); - assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM)); + assertTrue(mService.isBackupServiceActive(NON_SYSTEM_USER)); } @Test - public void - isBackupServiceActive_forUnstartedNonSystemUser_returnsTrueWhenSystemAndUserActivated() - throws Exception { - mService.setBackupServiceActive(UNSTARTED_NON_USER_SYSTEM, true); - - assertTrue(mService.isBackupServiceActive(UNSTARTED_NON_USER_SYSTEM)); + public void isBackupServiceActive_nonSystemUser_isNotDefault_notActivated_returnsFalse() { + // By default non-system non-default users are not activated. + assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER)); } @Test - public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() { + public void isBackupServiceActive_nonSystemUser_isNotDefault_activated_returnsTrue() { + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + + assertTrue(mService.isBackupServiceActive(NON_SYSTEM_USER)); + } + + @Test + public void setBackupServiceActive_forSystemUserAndCallerSystemUid_createsService() { BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID; mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + assertTrue(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM)); } @Test - public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() { + public void setBackupServiceActive_forSystemUserAndCallerRootUid_createsService() { BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID; mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + assertTrue(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM)); } @Test @@ -305,23 +289,25 @@ public class BackupManagerServiceTest { } @Test - public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() { + public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_createsService() { + simulateUserUnlocked(NON_SYSTEM_USER); when(mUserInfoMock.isManagedProfile()).thenReturn(true); BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID; - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); - assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM)); + assertTrue(mService.isUserReadyForBackup(NON_SYSTEM_USER)); } @Test - public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() { + public void setBackupServiceActive_forManagedProfileAndCallerRootUid_createsService() { + simulateUserUnlocked(NON_SYSTEM_USER); when(mUserInfoMock.isManagedProfile()).thenReturn(true); BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID; - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); - assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM)); + assertTrue(mService.isUserReadyForBackup(NON_SYSTEM_USER)); } @Test @@ -330,7 +316,7 @@ public class BackupManagerServiceTest { BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID; try { - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); fail(); } catch (SecurityException expected) { } @@ -343,7 +329,7 @@ public class BackupManagerServiceTest { .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString()); try { - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); fail(); } catch (SecurityException expected) { } @@ -357,14 +343,14 @@ public class BackupManagerServiceTest { eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString()); try { - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); fail(); } catch (SecurityException expected) { } } @Test - public void setBackupServiceActive_backupDisabled_ignored() { + public void setBackupServiceActive_backupDisabledGlobally_ignored() { BackupManagerServiceTestable.sBackupDisabled = true; BackupManagerServiceTestable service = new BackupManagerServiceTestable(mContextMock, mUserServices); @@ -384,77 +370,139 @@ public class BackupManagerServiceTest { } @Test - public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() { - mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); + public void setBackupServiceActive_systemUser_makeActive_deletesSuppressFile() { mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); - assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); - } - - @Test - public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() { mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + assertFalse(getFakeSuppressFileForUser(UserHandle.USER_SYSTEM).exists()); } @Test - public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated() - throws IOException { - assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); - + public void setBackupServiceActive_systemUser_makeNonActive_createsSuppressFile() { mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); - assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM)); + assertTrue(getFakeSuppressFileForUser(UserHandle.USER_SYSTEM).exists()); } @Test - public void setBackupActive_nonSystemUser_disabledForSystemUser_ignored() { - mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + public void setBackupServiceActive_systemUser_makeNonActive_stopsUserService() { + assertTrue(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM)); - assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM)); + mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false); + + assertFalse(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM)); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isDefault_makeActive_createsService() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + simulateUserUnlocked(NON_SYSTEM_USER); + mService.setBackupServiceActive(NON_SYSTEM_USER, false); + + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + + assertTrue(mService.isUserReadyForBackup(NON_SYSTEM_USER)); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isDefault_makeActive_deletesSuppressFile() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + simulateUserUnlocked(NON_SYSTEM_USER); + mService.setBackupServiceActive(NON_SYSTEM_USER, false); + + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + + assertFalse(getFakeSuppressFileForUser(NON_SYSTEM_USER).exists()); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isDefault_makeNonActive_createsSuppressFile() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + mService.setBackupServiceActive(NON_SYSTEM_USER, false); + + assertTrue(getFakeSuppressFileForUser(NON_SYSTEM_USER).exists()); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isDefault_makeNonActive_stopsUserService() { + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + simulateUserUnlocked(NON_SYSTEM_USER); + assertTrue(mService.isUserReadyForBackup(NON_SYSTEM_USER)); + + mService.setBackupServiceActive(NON_SYSTEM_USER, false); + + assertFalse(mService.isUserReadyForBackup(NON_SYSTEM_USER)); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isNotDefault_makeActive_createsService() { + simulateUserUnlocked(NON_SYSTEM_USER); + + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + + assertTrue(mService.isUserReadyForBackup(NON_SYSTEM_USER)); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isNotDefault_makeActive_createActivatedFile() { + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + + assertTrue(getFakeActivatedFileForUser(NON_SYSTEM_USER).exists()); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isNotDefault_makeNonActive_stopsUserService() { + simulateUserUnlocked(NON_SYSTEM_USER); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + + mService.setBackupServiceActive(NON_SYSTEM_USER, false); + + assertFalse(mService.isUserReadyForBackup(NON_SYSTEM_USER)); + } + + @Test + public void setBackupServiceActive_nonSystemUser_isNotDefault_makeActive_deleteActivatedFile() { + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + + mService.setBackupServiceActive(NON_SYSTEM_USER, false); + + assertFalse(getFakeActivatedFileForUser(NON_SYSTEM_USER).exists()); } @Test public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() { - int otherUser = NON_USER_SYSTEM + 1; - File activateFile = new File(mTestDir, "activate-" + otherUser); - BackupManagerServiceTestable.sActivatedFiles.append(otherUser, activateFile); + int otherUser = NON_SYSTEM_USER + 1; mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true); - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); - assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM)); assertFalse(mService.isBackupServiceActive(otherUser)); - activateFile.delete(); } @Test public void setBackupServiceActive_forNonSystemUser_remembersActivated() { - - mService.setBackupServiceActive(NON_USER_SYSTEM, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); assertTrue(RandomAccessFileUtils.readBoolean( - BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), false)); + getFakeRememberActivatedFileForUser(NON_SYSTEM_USER), false)); } @Test public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() { - - mService.setBackupServiceActive(NON_USER_SYSTEM, false); + mService.setBackupServiceActive(NON_SYSTEM_USER, false); assertFalse(RandomAccessFileUtils.readBoolean( - BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true)); + getFakeRememberActivatedFileForUser(NON_SYSTEM_USER), true)); } @Test public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() { - mService.setBackupServiceActive(NON_USER_SYSTEM, true); - mService.setBackupServiceActive(NON_USER_SYSTEM, false); + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + mService.setBackupServiceActive(NON_SYSTEM_USER, false); assertFalse(RandomAccessFileUtils.readBoolean( - BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true)); + getFakeRememberActivatedFileForUser(NON_SYSTEM_USER), true)); } @Test @@ -468,6 +516,7 @@ public class BackupManagerServiceTest { public void onSuccess(String transportName) { future.completeExceptionally(new AssertionError()); } + @Override public void onFailure(int reason) { future.complete(reason); @@ -488,13 +537,14 @@ public class BackupManagerServiceTest { } @Test - public void - selectBackupTransportAsyncForUser_beforeUserUnlockedListenerThrowing_doesNotThrow() + public void selectBackupTransportAsyncForUser_beforeUserUnlockedListenerThrowing_doesNotThrow() throws Exception { ISelectBackupTransportCallback.Stub listener = new ISelectBackupTransportCallback.Stub() { @Override - public void onSuccess(String transportName) {} + public void onSuccess(String transportName) { + } + @Override public void onFailure(int reason) throws RemoteException { throw new RemoteException(); @@ -509,13 +559,13 @@ public class BackupManagerServiceTest { @Test public void dump_callerDoesNotHaveDumpPermission_ignored() { when(mContextMock.checkCallingOrSelfPermission( - android.Manifest.permission.DUMP)).thenReturn( + Manifest.permission.DUMP)).thenReturn( PackageManager.PERMISSION_DENIED); mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]); - verifyNoMoreInteractions(mUserBackupManagerService); - verifyNoMoreInteractions(mNonSystemUserBackupManagerService); + verify(mSystemUserBackupManagerService, never()).dump(any(), any(), any()); + verify(mNonSystemUserBackupManagerService, never()).dump(any(), any(), any()); } @Test @@ -526,22 +576,24 @@ public class BackupManagerServiceTest { mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]); - verifyNoMoreInteractions(mUserBackupManagerService); - verifyNoMoreInteractions(mNonSystemUserBackupManagerService); + verify(mSystemUserBackupManagerService, never()).dump(any(), any(), any()); + verify(mNonSystemUserBackupManagerService, never()).dump(any(), any(), any()); } /** - * Test that {@link BackupManagerService#dump()} dumps system user information before non-system - * user information. + * Test that {@link BackupManagerService#dump(FileDescriptor, PrintWriter, String[])} dumps + * system user information before non-system user information. */ @Test public void testDump_systemUserFirst() { + mService.setBackupServiceActive(NON_SYSTEM_USER, true); + simulateUserUnlocked(NON_SYSTEM_USER); String[] args = new String[0]; mService.dumpWithoutCheckingPermission(mFileDescriptorStub, mPrintWriterMock, args); InOrder inOrder = - inOrder(mUserBackupManagerService, mNonSystemUserBackupManagerService); - inOrder.verify(mUserBackupManagerService) + inOrder(mSystemUserBackupManagerService, mNonSystemUserBackupManagerService); + inOrder.verify(mSystemUserBackupManagerService) .dump(mFileDescriptorStub, mPrintWriterMock, args); inOrder.verify(mNonSystemUserBackupManagerService) .dump(mFileDescriptorStub, mPrintWriterMock, args); @@ -550,30 +602,27 @@ public class BackupManagerServiceTest { @Test public void testGetUserForAncestralSerialNumber_forSystemUser() { - BackupManagerServiceTestable.sBackupDisabled = false; - BackupManagerService backupManagerService = - new BackupManagerServiceTestable(mContextMock, mUserServices); + simulateUserUnlocked(NON_SYSTEM_USER); when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false)) - .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM}); - when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L); + .thenReturn(new int[]{UserHandle.USER_SYSTEM, NON_SYSTEM_USER}); + when(mSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L); - UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L); + UserHandle user = mService.getUserForAncestralSerialNumber(11L); assertThat(user).isEqualTo(UserHandle.of(UserHandle.USER_SYSTEM)); } @Test public void testGetUserForAncestralSerialNumber_forNonSystemUser() { - BackupManagerServiceTestable.sBackupDisabled = false; - BackupManagerService backupManagerService = - new BackupManagerServiceTestable(mContextMock, mUserServices); + setMockMainUserAndStartNewBackupManagerService(NON_SYSTEM_USER); + simulateUserUnlocked(NON_SYSTEM_USER); when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false)) - .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM}); + .thenReturn(new int[]{UserHandle.USER_SYSTEM, NON_SYSTEM_USER}); when(mNonSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L); - UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L); + UserHandle user = mService.getUserForAncestralSerialNumber(11L); - assertThat(user).isEqualTo(UserHandle.of(NON_USER_SYSTEM)); + assertThat(user).isEqualTo(UserHandle.of(NON_SYSTEM_USER)); } @Test @@ -581,20 +630,46 @@ public class BackupManagerServiceTest { BackupManagerServiceTestable.sBackupDisabled = true; BackupManagerService backupManagerService = new BackupManagerServiceTestable(mContextMock, mUserServices); - when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L); + when(mSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L); UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L); assertThat(user).isNull(); } + /** + * The 'default' user is set in the constructor of {@link BackupManagerService} so we need to + * start a new service after mocking the 'main' user. + */ + private void setMockMainUserAndStartNewBackupManagerService(int userId) { + when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(userId)); + mService = new BackupManagerServiceTestable(mContextMock, mUserServices); + } + + private void simulateUserUnlocked(int userId) { + ConditionVariable unlocked = new ConditionVariable(false); + mService.onUnlockUser(userId); + mService.getBackupHandler().post(unlocked::open); + unlocked.block(); + when(mUserManagerMock.isUserUnlocked(userId)).thenReturn(true); + } + + private static File getFakeSuppressFileForUser(int userId) { + return new File(sTestDir, "suppress-" + userId); + } + + private static File getFakeActivatedFileForUser(int userId) { + return new File(sTestDir, "activated-" + userId); + } + + private static File getFakeRememberActivatedFileForUser(int userId) { + return new File(sTestDir, "rememberActivated-" + userId); + } + private static class BackupManagerServiceTestable extends BackupManagerService { static boolean sBackupDisabled = false; static int sCallingUserId = -1; static int sCallingUid = -1; - static File sSuppressFile = null; - static SparseArray sActivatedFiles = new SparseArray<>(); - static SparseArray sRememberActivatedFiles = new SparseArray<>(); static UserManager sUserManagerMock = null; BackupManagerServiceTestable( @@ -613,20 +688,21 @@ public class BackupManagerServiceTest { } @Override - protected File getSuppressFileForSystemUser() { - return sSuppressFile; + protected File getSuppressFileForUser(int userId) { + return getFakeSuppressFileForUser(userId); } @Override protected File getRememberActivatedFileForNonSystemUser(int userId) { - return sRememberActivatedFiles.get(userId); + return getFakeRememberActivatedFileForUser(userId); } @Override - protected File getActivatedFileForNonSystemUser(int userId) { - return sActivatedFiles.get(userId); + protected File getActivatedFileForUser(int userId) { + return getFakeActivatedFileForUser(userId); } + @Override protected int binderGetCallingUserId() { return sCallingUserId; }