summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnnie Meng <anniemeng@google.com>2019-01-22 15:32:25 +0000
committerAnnie Meng <anniemeng@google.com>2019-01-24 15:14:09 +0000
commitbdb8848abe9999fbd302d71f2c0ce62b9a09fd8a (patch)
treeb2c8d96aeb9ee6e07dc9434cabe619c8857b50da
parent25b54058c0e3bb7e0630650f750092f7ccd2289f (diff)
[Multi-user] Disable backup by default in non-system users
Key changes in this CL: - Backup is now disabled by default in non-system users unless DPM activates backup for this user AND the system user is activated. This provides gating for the multi-user B&R feature. - Activation is done via an 'activate' file that is per-user (but lives in the system user directory to account for locked users). - isBackupServiceActive() handles both locked and unlocked users. - Added a bmgr command to expose isBackupServiceActive() for testing purposes and enforce appropriate permissions. Future CLs: - Handle future migration to backup on by default for non-system users - Change CTS tests to use the new bmgr command Bug: 121306407 Test: 1) atest TrampolineTest 2) Start system user -> service started; run backup and restore successfully 3) Start non-system user -> ignored; 4) adb shell bmgr --user 0 activate true -> security exception; adb shell bmgr --user 10 activate true -> security exception (work profile); adb shell bmgr --user 11 activate true/false -> creates/deletes activate file and starts/stops the service Change-Id: Ic77db9b8b2e5170dcf89bef863dac4713730797a
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java46
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java195
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java290
3 files changed, 331 insertions, 200 deletions
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 3defdc5467c8..062ba655640e 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -102,7 +102,17 @@ public class Bmgr {
String op = nextArg();
Slog.v(TAG, "Running " + op + " for user:" + userId);
- if (!isBmgrActive(userId)) {
+ if (mBmgr == null) {
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ return;
+ }
+
+ if ("activate".equals(op)) {
+ doActivateService(userId);
+ return;
+ }
+
+ if (!isBackupActive(userId)) {
return;
}
@@ -175,12 +185,7 @@ public class Bmgr {
showUsage();
}
- boolean isBmgrActive(@UserIdInt int userId) {
- if (mBmgr == null) {
- System.err.println(BMGR_NOT_RUNNING_ERR);
- return false;
- }
-
+ boolean isBackupActive(@UserIdInt int userId) {
try {
if (!mBmgr.isBackupServiceActive(userId)) {
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -845,6 +850,27 @@ public class Bmgr {
}
}
+ private void doActivateService(int userId) {
+ String arg = nextArg();
+ if (arg == null) {
+ showUsage();
+ return;
+ }
+
+ try {
+ boolean activate = Boolean.parseBoolean(arg);
+ mBmgr.setBackupServiceActive(userId, activate);
+ System.out.println(
+ "Backup service now "
+ + (activate ? "activated" : "deactivated")
+ + " for user "
+ + userId);
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
@@ -880,6 +906,7 @@ public class Bmgr {
System.err.println(" bmgr backupnow [--monitor|--monitor-verbose] --all|PACKAGE...");
System.err.println(" bmgr cancel backups");
System.err.println(" bmgr init TRANSPORT...");
+ System.err.println(" bmgr activate BOOL");
System.err.println("");
System.err.println("The '--user' option specifies the user on which the operation is run.");
System.err.println("It must be the first argument before the operation.");
@@ -946,6 +973,11 @@ public class Bmgr {
System.err.println("");
System.err.println("The 'init' command initializes the given transports, wiping all data");
System.err.println("from their backing data stores.");
+ System.err.println("");
+ System.err.println("The 'activate' command activates or deactivates the backup service.");
+ System.err.println("If the argument is 'true' it will be activated, otherwise it will be");
+ System.err.println("deactivated. When deactivated, the service will not be running and no");
+ System.err.println("operations can be performed until activation.");
}
private static class BackupMonitor extends IBackupManagerMonitor.Stub {
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index b9a6f3c08cc4..603c589cd5f6 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -18,6 +18,7 @@ package com.android.server.backup;
import static com.android.server.backup.BackupManagerService.TAG;
+import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
@@ -41,9 +42,10 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.os.UserManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import java.io.File;
@@ -75,19 +77,25 @@ import java.io.PrintWriter;
* system user is unlocked before any other users.
*/
public class Trampoline extends IBackupManager.Stub {
- // When this file is present, the backup service is inactive.
+ /**
+ * Name of file that disables the backup service. If this file exists, then backup is disabled
+ * for all users.
+ */
private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
+ /**
+ * Name of file for non-system users that enables the backup service for the user. Backup is
+ * disabled by default in non-system users.
+ */
+ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
+
// Product-level suppression of backup/restore.
private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
private static final String BACKUP_THREAD = "backup";
- /** Values for setting {@link Settings.Global#BACKUP_MULTI_USER_ENABLED} */
- private static final int MULTI_USER_DISABLED = 0;
- private static final int MULTI_USER_ENABLED = 1;
-
private final Context mContext;
+ private final UserManager mUserManager;
private final boolean mGlobalDisable;
// Lock to write backup suppress files.
@@ -104,20 +112,13 @@ public class Trampoline extends IBackupManager.Stub {
mHandlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
+ mUserManager = UserManager.get(context);
}
protected boolean isBackupDisabled() {
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
- private boolean isMultiUserEnabled() {
- return Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.BACKUP_MULTI_USER_ENABLED,
- MULTI_USER_DISABLED)
- == MULTI_USER_ENABLED;
- }
-
protected int binderGetCallingUserId() {
return Binder.getCallingUserHandle().getIdentifier();
}
@@ -126,21 +127,65 @@ public class Trampoline extends IBackupManager.Stub {
return Binder.getCallingUid();
}
- protected File getSuppressFileForUser(int userId) {
- return new File(UserBackupManagerFiles.getBaseStateDir(userId),
+ /** Stored in the system user's directory. */
+ protected File getSuppressFileForSystemUser() {
+ return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
BACKUP_SUPPRESS_FILENAME);
}
- protected void createBackupSuppressFileForUser(int userId) throws IOException {
- synchronized (mStateLock) {
- getSuppressFileForUser(userId).getParentFile().mkdirs();
- getSuppressFileForUser(userId).createNewFile();
+ /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
+ BACKUP_ACTIVATED_FILENAME + "-" + userId);
+ }
+
+ private void createFile(File file) throws IOException {
+ if (file.exists()) {
+ return;
+ }
+
+ file.getParentFile().mkdirs();
+ if (!file.createNewFile()) {
+ Slog.w(TAG, "Failed to create file " + file.getPath());
}
}
- private void deleteBackupSuppressFileForUser(int userId) {
- if (!getSuppressFileForUser(userId).delete()) {
- Slog.w(TAG, "Failed deleting backup suppressed file for user: " + userId);
+ private void deleteFile(File file) {
+ if (!file.exists()) {
+ return;
+ }
+
+ if (!file.delete()) {
+ Slog.w(TAG, "Failed to delete file " + file.getPath());
+ }
+ }
+
+ /**
+ * 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.
+ */
+ @GuardedBy("mStateLock")
+ private void deactivateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ createFile(getSuppressFileForSystemUser());
+ } else {
+ deleteFile(getActivatedFileForNonSystemUser(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.
+ */
+ @GuardedBy("mStateLock")
+ private void activateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ deleteFile(getSuppressFileForSystemUser());
+ } else {
+ createFile(getActivatedFileForNonSystemUser(userId));
}
}
@@ -148,24 +193,31 @@ public class Trampoline extends IBackupManager.Stub {
// admin (device owner or profile owner).
private boolean isUserReadyForBackup(int userId) {
return mService != null && mService.getServiceUsers().get(userId) != null
- && !isBackupSuppressedForUser(userId);
+ && isBackupActivatedForUser(userId);
}
- private boolean isBackupSuppressedForUser(int userId) {
- // If backup is disabled for system user, it's disabled for all other users on device.
- if (getSuppressFileForUser(UserHandle.USER_SYSTEM).exists()) {
- return true;
- }
- if (userId != UserHandle.USER_SYSTEM) {
- return getSuppressFileForUser(userId).exists();
+ /**
+ * 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.
+ */
+ private boolean isBackupActivatedForUser(int userId) {
+ if (getSuppressFileForSystemUser().exists()) {
+ return false;
}
- return false;
+
+ return userId == UserHandle.USER_SYSTEM
+ || getActivatedFileForNonSystemUser(userId).exists();
}
protected Context getContext() {
return mContext;
}
+ protected UserManager getUserManager() {
+ return mUserManager;
+ }
+
protected BackupManagerService createBackupManagerService() {
return new BackupManagerService(mContext, this, mHandlerThread);
}
@@ -198,23 +250,17 @@ public class Trampoline extends IBackupManager.Stub {
/**
* Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
- * Starts the backup service for this user if it's the system user or if the service supports
- * multi-user. Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time
- * low.
+ * Starts the backup service for this user if backup is active for this user. Offloads work onto
+ * the handler thread {@link #mHandlerThread} to keep unlock time low.
*/
void unlockUser(int userId) {
- if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
- Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
- return;
- }
-
postToHandler(() -> startServiceForUser(userId));
}
private void startServiceForUser(int userId) {
// We know that the user is unlocked here because it is called from setBackupServiceActive
// and unlockUser which have these guarantees. So we can check if the file exists.
- if (mService != null && !isBackupSuppressedForUser(userId)) {
+ if (mService != null && isBackupActivatedForUser(userId)) {
Slog.i(TAG, "Starting service for user: " + userId);
mService.startServiceForUser(userId);
}
@@ -225,11 +271,6 @@ public class Trampoline extends IBackupManager.Stub {
* Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
*/
void stopUser(int userId) {
- if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
- Slog.i(TAG, "Multi-user disabled, cannot stop service for user: " + userId);
- return;
- }
-
postToHandler(
() -> {
if (mService != null) {
@@ -240,45 +281,63 @@ public class Trampoline extends IBackupManager.Stub {
}
/**
- * Only privileged callers should be changing the backup state. This method only acts on {@link
- * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
- * system user also deactivates backup in all users.
- *
- * This call will only work if the calling {@code userID} is unlocked.
+ * The system user and managed profiles can only be acted on by callers in the system or root
+ * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
+ * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
*/
- public void setBackupServiceActive(int userId, boolean makeActive) {
- int caller = binderGetCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
- throw new SecurityException("No permission to configure backup activity");
+ private void enforcePermissionsOnUser(int userId) throws SecurityException {
+ boolean isRestrictedUser =
+ userId == UserHandle.USER_SYSTEM
+ || getUserManager().getUserInfo(userId).isManagedProfile();
+
+ if (isRestrictedUser) {
+ int caller = binderGetCallingUid();
+ if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
+ throw new SecurityException("No permission to configure backup activity");
+ }
+ } else {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.BACKUP, "No permission to configure backup activity");
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "No permission to configure backup activity");
}
+ }
+
+ /**
+ * Only privileged callers should be changing the backup state. Deactivating backup in the
+ * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
+ * is unlocked at this point yet, so handle both cases.
+ */
+ public void setBackupServiceActive(int userId, boolean makeActive) {
+ enforcePermissionsOnUser(userId);
if (mGlobalDisable) {
Slog.i(TAG, "Backup service not supported");
return;
}
- if (userId != UserHandle.USER_SYSTEM) {
- Slog.i(TAG, "Cannot set backup service activity for non-system user: " + userId);
- return;
- }
-
- if (makeActive == isBackupServiceActive(userId)) {
- Slog.i(TAG, "No change in backup service activity");
- return;
- }
-
synchronized (mStateLock) {
Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
if (makeActive) {
if (mService == null) {
mService = createBackupManagerService();
}
- deleteBackupSuppressFileForUser(userId);
- startServiceForUser(userId);
+ try {
+ activateBackupForUserLocked(userId);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service activity");
+ }
+
+ // If the user is unlocked, we can start the backup service for it. Otherwise we
+ // will start the service when the user is unlocked as part of its unlock callback.
+ if (getUserManager().isUserUnlocked(userId)) {
+ startServiceForUser(userId);
+ }
} else {
try {
//TODO(b/121198006): what if this throws an exception?
- createBackupSuppressFileForUser(userId);
+ deactivateBackupForUserLocked(userId);
} catch (IOException e) {
Slog.e(TAG, "Unable to persist backup service inactivity");
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index ac4a5fe90c06..a3f36b720398 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -24,11 +24,15 @@ import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
@@ -39,21 +43,21 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
import android.util.SparseArray;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.test.FakeSettingsProvider;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -99,10 +103,6 @@ public class TrampolineTest {
@Mock
private Context mContextMock;
@Mock
- private File mSuppressFileMock;
- @Mock
- private File mSuppressFileParentMock;
- @Mock
private IBinder mAgentMock;
@Mock
private ParcelFileDescriptor mParcelFileDescriptorMock;
@@ -114,181 +114,232 @@ public class TrampolineTest {
private IBackupManagerMonitor mBackupManagerMonitorMock;
@Mock
private PrintWriter mPrintWriterMock;
+ @Mock
+ private UserManager mUserManagerMock;
+ @Mock
+ private UserInfo mUserInfoMock;
private FileDescriptor mFileDescriptorStub = new FileDescriptor();
private TrampolineTestable mTrampoline;
- private MockContentResolver mContentResolver;
+ private File mTestDir;
+ private File mSuppressFile;
+ private File mActivatedFile;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mUserId = NON_USER_SYSTEM;
+ mUserId = UserHandle.USER_SYSTEM;
SparseArray<UserBackupManagerService> serviceUsers = new SparseArray<>();
- serviceUsers.append(UserHandle.SYSTEM.getIdentifier(), mUserBackupManagerService);
+ serviceUsers.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
serviceUsers.append(NON_USER_SYSTEM, mUserBackupManagerService);
when(mBackupManagerServiceMock.getServiceUsers()).thenReturn(serviceUsers);
+ when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
+ when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
+
TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
TrampolineTestable.sBackupDisabled = false;
+ TrampolineTestable.sUserManagerMock = mUserManagerMock;
+
+ mTestDir = InstrumentationRegistry.getContext().getFilesDir();
+ mTestDir.mkdirs();
- when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
+ mSuppressFile = new File(mTestDir, "suppress");
+ TrampolineTestable.sSuppressFile = mSuppressFile;
+
+ mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM);
+ TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile);
mTrampoline = new TrampolineTestable(mContextMock);
+ }
- mContentResolver = new MockContentResolver();
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContextMock.getContentResolver()).thenReturn(mContentResolver);
+ @After
+ public void tearDown() throws Exception {
+ mSuppressFile.delete();
+ mActivatedFile.delete();
}
@Test
- public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ public void initializeService_successfullyInitializesBackupService() {
mTrampoline.initializeService();
- mTrampoline.unlockUser(UserHandle.USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).startServiceForUser(UserHandle.USER_SYSTEM);
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
- mTrampoline.initializeService();
+ public void initializeService_globallyDisabled_nonInitialized() {
+ TrampolineTestable.sBackupDisabled = true;
+ TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
- mTrampoline.unlockUser(NON_USER_SYSTEM);
+ trampoline.initializeService();
- verify(mBackupManagerServiceMock, never()).startServiceForUser(NON_USER_SYSTEM);
+ assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+ public void initializeService_doesNotStartServiceForUsers() {
mTrampoline.initializeService();
- mTrampoline.unlockUser(NON_USER_SYSTEM);
+ verify(mBackupManagerServiceMock, never()).startServiceForUser(anyInt());
+ }
- verify(mBackupManagerServiceMock).startServiceForUser(NON_USER_SYSTEM);
+ @Test
+ public void isBackupServiceActive_calledBeforeInitialize_returnsFalse() {
+ assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- mTrampoline.stopUser(UserHandle.USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM);
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
- mTrampoline.stopUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock, never()).stopServiceForUser(NON_USER_SYSTEM);
+ assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
-
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
+ throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- mTrampoline.stopUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).stopServiceForUser(NON_USER_SYSTEM);
+ assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void initializeService_successfullyInitializesBackupService() {
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
+ throws Exception {
mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ // Don't activate non-system user.
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void initializeService_globallyDisabled_nonInitialized() {
- TrampolineTestable.sBackupDisabled = true;
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-
- trampoline.initializeService();
+ public void
+ isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
+ throws Exception {
+ mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
- // Verify that BackupManagerService is not initialized if suppress file exists.
@Test
- public void initializeService_suppressFileExists_nonInitialized() throws Exception {
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
- trampoline.createBackupSuppressFileForUser(UserHandle.USER_SYSTEM);
-
+ public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
+ mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
- trampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void initializeService_doesNotStartServiceForUsers() {
+ public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.ROOT_UID;
- verify(mBackupManagerServiceMock, never()).startServiceForUser(anyInt());
- }
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- @Test
- public void isBackupServiceActive_calledBeforeInitialize_returnsFalse() {
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
- public void isBackupServiceActive_forNonSysUser_whenSysUserIsDeactivated_returnsFalse() {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
+ mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ try {
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
}
@Test
- public void setBackupServiceActive_callerSystemUid_serviceCreated() {
+ public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void setBackupServiceActive_callerRootUid_serviceCreated() {
+ public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.ROOT_UID;
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void setBackupServiceActive_callerNonRootNonSystem_securityExceptionThrown() {
+ public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
try {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
fail();
} catch (SecurityException expected) {
}
+ }
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
+ mTrampoline.initializeService();
+
+ try {
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(
+ eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
+ mTrampoline.initializeService();
+
+ try {
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
}
@Test
public void setBackupServiceActive_backupDisabled_ignored() {
TrampolineTestable.sBackupDisabled = true;
TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
+ trampoline.initializeService();
trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -296,14 +347,8 @@ public class TrampolineTest {
}
@Test
- public void setBackupServiceActive_nonUserSystem_ignored() {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
public void setBackupServiceActive_alreadyActive_ignored() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
assertEquals(1, mTrampoline.getCreateServiceCallsCount());
@@ -315,6 +360,7 @@ public class TrampolineTest {
@Test
public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -323,6 +369,7 @@ public class TrampolineTest {
@Test
public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
@@ -349,6 +396,21 @@ public class TrampolineTest {
}
@Test
+ public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
+ mTrampoline.initializeService();
+ int otherUser = NON_USER_SYSTEM + 1;
+ File activateFile = new File(mTestDir, "activate-" + otherUser);
+ TrampolineTestable.sActivatedFiles.append(otherUser, activateFile);
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ assertFalse(mTrampoline.isBackupServiceActive(otherUser));
+ activateFile.delete();
+ }
+
+ @Test
public void dataChanged_calledBeforeInitialize_ignored() throws Exception {
mTrampoline.dataChanged(PACKAGE_NAME);
verifyNoMoreInteractions(mBackupManagerServiceMock);
@@ -1123,7 +1185,7 @@ public class TrampolineTest {
@Test
public void requestBackup_forwardedToCallingUserId() throws Exception {
TrampolineTestable.sCallingUserId = mUserId;
- when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
+ when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
mTrampoline.initializeService();
@@ -1227,50 +1289,33 @@ public class TrampolineTest {
static int sCallingUserId = -1;
static int sCallingUid = -1;
static BackupManagerService sBackupManagerServiceMock = null;
+ static File sSuppressFile = null;
+ static SparseArray<File> sActivatedFiles = new SparseArray<>();
+ static UserManager sUserManagerMock = null;
private int mCreateServiceCallsCount = 0;
- private SparseArray<FakeFile> mSuppressFiles = new SparseArray<>();
-
- private static class FakeFile extends File {
- private boolean mExists;
-
- FakeFile(String pathname) {
- super(pathname);
- }
-
- @Override
- public boolean exists() {
- return mExists;
- }
-
- @Override
- public boolean delete() {
- mExists = false;
- return true;
- }
-
- @Override
- public boolean createNewFile() throws IOException {
- mExists = true;
- return true;
- }
- }
TrampolineTestable(Context context) {
super(context);
}
@Override
+ protected UserManager getUserManager() {
+ return sUserManagerMock;
+ }
+
+ @Override
public boolean isBackupDisabled() {
return sBackupDisabled;
}
@Override
- public File getSuppressFileForUser(int userId) {
- if (mSuppressFiles.get(userId) == null) {
- FakeFile file = new FakeFile(Integer.toString(userId));
- mSuppressFiles.append(userId, file);
- }
- return mSuppressFiles.get(userId);
+ protected File getSuppressFileForSystemUser() {
+ return sSuppressFile;
+ }
+
+ @Override
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return sActivatedFiles.get(userId);
}
protected int binderGetCallingUserId() {
@@ -1289,11 +1334,6 @@ public class TrampolineTest {
}
@Override
- protected void createBackupSuppressFileForUser(int userId) throws IOException {
- getSuppressFileForUser(userId).createNewFile();
- }
-
- @Override
protected void postToHandler(Runnable runnable) {
runnable.run();
}