[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
This commit is contained in:
Annie Meng
2019-01-22 15:32:25 +00:00
parent 25b54058c0
commit bdb8848abe
3 changed files with 345 additions and 214 deletions

View File

@@ -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 {

View File

@@ -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;
/**
* 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;
}
if (userId != UserHandle.USER_SYSTEM) {
return getSuppressFileForUser(userId).exists();
}
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.
*/
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) {
int caller = binderGetCallingUid();
if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
throw new SecurityException("No permission to configure backup activity");
}
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");
}

View File

@@ -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,95 +114,53 @@ 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;
when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
mTestDir = InstrumentationRegistry.getContext().getFilesDir();
mTestDir.mkdirs();
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);
}
@Test
public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
mTrampoline.initializeService();
mTrampoline.unlockUser(UserHandle.USER_SYSTEM);
verify(mBackupManagerServiceMock).startServiceForUser(UserHandle.USER_SYSTEM);
}
@Test
public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
mTrampoline.initializeService();
mTrampoline.unlockUser(NON_USER_SYSTEM);
verify(mBackupManagerServiceMock, never()).startServiceForUser(NON_USER_SYSTEM);
}
@Test
public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
mTrampoline.initializeService();
mTrampoline.unlockUser(NON_USER_SYSTEM);
verify(mBackupManagerServiceMock).startServiceForUser(NON_USER_SYSTEM);
}
@Test
public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
mTrampoline.initializeService();
mTrampoline.stopUser(UserHandle.USER_SYSTEM);
verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM);
}
@Test
public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
mTrampoline.initializeService();
mTrampoline.stopUser(NON_USER_SYSTEM);
verify(mBackupManagerServiceMock, never()).stopServiceForUser(NON_USER_SYSTEM);
}
@Test
public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
mTrampoline.initializeService();
mTrampoline.stopUser(NON_USER_SYSTEM);
verify(mBackupManagerServiceMock).stopServiceForUser(NON_USER_SYSTEM);
@After
public void tearDown() throws Exception {
mSuppressFile.delete();
mActivatedFile.delete();
}
@Test
@@ -222,18 +180,6 @@ public class TrampolineTest {
assertFalse(trampoline.isBackupServiceActive(UserHandle.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);
trampoline.initializeService();
assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
public void initializeService_doesNotStartServiceForUsers() {
mTrampoline.initializeService();
@@ -247,15 +193,55 @@ public class TrampolineTest {
}
@Test
public void isBackupServiceActive_forNonSysUser_whenSysUserIsDeactivated_returnsFalse() {
mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
throws Exception {
mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
public void setBackupServiceActive_callerSystemUid_serviceCreated() {
public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
throws Exception {
mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
// Don't activate non-system user.
assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
public void
isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
throws Exception {
mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -264,7 +250,8 @@ public class TrampolineTest {
}
@Test
public void setBackupServiceActive_callerRootUid_serviceCreated() {
public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.ROOT_UID;
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -273,7 +260,8 @@ public class TrampolineTest {
}
@Test
public void setBackupServiceActive_callerNonRootNonSystem_securityExceptionThrown() {
public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
try {
@@ -281,29 +269,86 @@ public class TrampolineTest {
fail();
} catch (SecurityException expected) {
}
}
assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
@Test
public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
when(mUserInfoMock.isManagedProfile()).thenReturn(true);
mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
when(mUserInfoMock.isManagedProfile()).thenReturn(true);
mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.ROOT_UID;
mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
when(mUserInfoMock.isManagedProfile()).thenReturn(true);
mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
try {
mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
fail();
} catch (SecurityException expected) {
}
}
@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);
assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@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));
@@ -348,6 +395,21 @@ public class TrampolineTest {
assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@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);
@@ -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() {
@@ -1288,11 +1333,6 @@ public class TrampolineTest {
return sBackupManagerServiceMock;
}
@Override
protected void createBackupSuppressFileForUser(int userId) throws IOException {
getSuppressFileForUser(userId).createNewFile();
}
@Override
protected void postToHandler(Runnable runnable) {
runnable.run();