[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:
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user