diff options
7 files changed, 377 insertions, 44 deletions
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 3723fce9d4f0..e3748f1653ab 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -201,7 +201,7 @@ public class Bmgr { private void doEnabled(@UserIdInt int userId) { try { - boolean isEnabled = mBmgr.isBackupEnabled(); + boolean isEnabled = mBmgr.isBackupEnabledForUser(userId); System.out.println("Backup Manager currently " + enableToString(isEnabled)); } catch (RemoteException e) { @@ -219,7 +219,7 @@ public class Bmgr { try { boolean enable = Boolean.parseBoolean(arg); - mBmgr.setBackupEnabled(enable); + mBmgr.setBackupEnabledForUser(userId, enable); System.out.println("Backup Manager now " + enableToString(enable)); } catch (NumberFormatException e) { showUsage(); @@ -232,7 +232,7 @@ public class Bmgr { void doRun(@UserIdInt int userId) { try { - mBmgr.backupNow(); + mBmgr.backupNowForUser(userId); } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(BMGR_NOT_RUNNING_ERR); @@ -416,7 +416,8 @@ public class Bmgr { (monitorState != Monitor.OFF) ? new BackupMonitor(monitorState == Monitor.VERBOSE) : null; - int err = mBmgr.requestBackup( + int err = mBmgr.requestBackupForUser( + userId, packages.toArray(new String[packages.size()]), observer, monitor, @@ -477,7 +478,7 @@ public class Bmgr { String arg = nextArg(); if ("backups".equals(arg)) { try { - mBmgr.cancelBackups(); + mBmgr.cancelBackupsForUser(userId); } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(BMGR_NOT_RUNNING_ERR); diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index 3e2074837b8e..0afb98f1ed84 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -91,6 +91,15 @@ interface IBackupManager { * at some point in the future. * * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * If {@code userId} is different from the calling user id, then the caller must hold the + * android.permission.INTERACT_ACROSS_USERS_FULL permission. + * + * @param userId User id for which backup service should be enabled/disabled. + */ + void setBackupEnabledForUser(int userId, boolean isEnabled); + + /** + * {@link android.app.backup.IBackupManager.setBackupEnabledForUser} for the calling user id. */ void setBackupEnabled(boolean isEnabled); @@ -120,6 +129,15 @@ interface IBackupManager { * Report whether the backup mechanism is currently enabled. * * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * If {@code userId} is different from the calling user id, then the caller must hold the + * android.permission.INTERACT_ACROSS_USERS_FULL permission. + * + * @param userId User id for which the backup service status should be reported. + */ + boolean isBackupEnabledForUser(int userId); + + /** + * {@link android.app.backup.IBackupManager.isBackupEnabledForUser} for the calling user id. */ boolean isBackupEnabled(); @@ -149,6 +167,15 @@ interface IBackupManager { * method. * * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * If {@code userId} is different from the calling user id, then the caller must hold the + * android.permission.INTERACT_ACROSS_USERS_FULL permission. + * + * @param userId User id for which an immediate backup should be scheduled. + */ + void backupNowForUser(int userId); + + /** + * {@link android.app.backup.IBackupManager.backupNowForUser} for the calling user id. */ void backupNow(); @@ -432,6 +459,12 @@ interface IBackupManager { * <p>If this method returns zero (meaning success), the OS will attempt to backup all provided * packages using the remote transport. * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * If {@code userId} is different from the calling user id, then the caller must hold the + * android.permission.INTERACT_ACROSS_USERS_FULL permission. + * + * @param userId User id for which an immediate backup should be requested. + * @param observer The {@link BackupObserver} to receive callbacks during the backup * operation. * @@ -442,12 +475,29 @@ interface IBackupManager { * * @return Zero on success; nonzero on error. */ + int requestBackupForUser(int userId, in String[] packages, IBackupObserver observer, + IBackupManagerMonitor monitor, int flags); + + /** + * {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id. + */ int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags); /** * Cancel all running backups. After this call returns, no currently running backups will * interact with the selected transport. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * If {@code userId} is different from the calling user id, then the caller must hold the + * android.permission.INTERACT_ACROSS_USERS_FULL permission. + * + * @param userId User id for which backups should be cancelled. + */ + void cancelBackupsForUser(int userId); + + /** + * {@link android.app.backup.IBackupManager.cancelBackups} for the calling user id. */ void cancelBackups(); } diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 3acdc8e3aeb9..a917ced2ae68 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -16,7 +16,9 @@ package com.android.server.backup; +import android.Manifest; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; @@ -30,6 +32,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.Binder; import android.os.Environment; import android.os.HandlerThread; import android.os.IBinder; @@ -79,6 +82,7 @@ public class BackupManagerService { return sInstance; } + private final Context mContext; private UserBackupManagerService mUserBackupManagerService; /** Instantiate a new instance of {@link BackupManagerService}. */ @@ -91,11 +95,26 @@ public class BackupManagerService { transportWhitelist = Collections.emptySet(); } + mContext = context; mUserBackupManagerService = UserBackupManagerService.createAndInitializeService( context, trampoline, backupThread, transportWhitelist); } + /** + * If {@code userId} is different from the calling user id, then the caller must hold the + * android.permission.INTERACT_ACROSS_USERS_FULL permission. + * + * @param userId User id on which the backup operation is being requested. + * @param message A message to include in the exception if it is thrown. + */ + private void enforceCallingPermissionOnUserId(int userId, String message) { + if (Binder.getCallingUserHandle().getIdentifier() != userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); + } + } + // TODO(b/118520567): Remove when tests are modified to use per-user instance. @VisibleForTesting void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) { @@ -314,7 +333,8 @@ public class BackupManagerService { // --------------------------------------------- /** Enable/disable the backup service. This is user-configurable via backup settings. */ - public void setBackupEnabled(boolean enable) { + public void setBackupEnabled(@UserIdInt int userId, boolean enable) { + enforceCallingPermissionOnUserId(userId, "setBackupEnabled"); mUserBackupManagerService.setBackupEnabled(enable); } @@ -331,7 +351,8 @@ public class BackupManagerService { /** * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}. */ - public boolean isBackupEnabled() { + public boolean isBackupEnabled(@UserIdInt int userId) { + enforceCallingPermissionOnUserId(userId, "isBackupEnabled"); return mUserBackupManagerService.isBackupEnabled(); } @@ -355,7 +376,8 @@ public class BackupManagerService { * Run a backup pass immediately for any key-value backup applications that have declared that * they have pending updates. */ - public void backupNow() { + public void backupNow(@UserIdInt int userId) { + enforceCallingPermissionOnUserId(userId, "backupNow"); mUserBackupManagerService.backupNow(); } @@ -364,12 +386,18 @@ public class BackupManagerService { * IBackupManagerMonitor} for receiving events during the operation. */ public int requestBackup( - String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) { + @UserIdInt int userId, + String[] packages, + IBackupObserver observer, + IBackupManagerMonitor monitor, + int flags) { + enforceCallingPermissionOnUserId(userId, "requestBackup"); return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags); } /** Cancel all running backup operations. */ - public void cancelBackups() { + public void cancelBackups(@UserIdInt int userId) { + enforceCallingPermissionOnUserId(userId, "cancelBackups"); mUserBackupManagerService.cancelBackups(); } @@ -533,8 +561,10 @@ public class BackupManagerService { stage.renameTo(enableFile); // will be synced immediately by the try-with-resources call to close() } catch (IOException | RuntimeException e) { - Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: " - + e.getMessage()); + Slog.e( + TAG, + "Unable to record backup enable state; reverting to disabled: " + + e.getMessage()); enableFile.delete(); stage.delete(); } diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 108f50d922f8..ed6ff9b1d77d 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -19,6 +19,7 @@ package com.android.server.backup; import static com.android.server.backup.BackupManagerService.TAG; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.admin.DevicePolicyManager; import android.app.backup.BackupManager; import android.app.backup.IBackupManager; @@ -123,6 +124,10 @@ public class Trampoline extends IBackupManager.Stub { == MULTI_USER_ENABLED; } + protected int binderGetCallingUserId() { + return Binder.getCallingUserHandle().getIdentifier(); + } + protected int binderGetCallingUid() { return Binder.getCallingUid(); } @@ -319,14 +324,20 @@ public class Trampoline extends IBackupManager.Stub { } @Override - public void setBackupEnabled(boolean isEnabled) throws RemoteException { + public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled) + throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.setBackupEnabled(isEnabled); + svc.setBackupEnabled(userId, isEnabled); } } @Override + public void setBackupEnabled(boolean isEnabled) throws RemoteException { + setBackupEnabledForUser(binderGetCallingUserId(), isEnabled); + } + + @Override public void setAutoRestore(boolean doAutoRestore) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { @@ -343,9 +354,14 @@ public class Trampoline extends IBackupManager.Stub { } @Override - public boolean isBackupEnabled() throws RemoteException { + public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.isBackupEnabled() : false; + return (svc != null) ? svc.isBackupEnabled(userId) : false; + } + + @Override + public boolean isBackupEnabled() throws RemoteException { + return isBackupEnabledForUser(binderGetCallingUserId()); } @Override @@ -361,14 +377,19 @@ public class Trampoline extends IBackupManager.Stub { } @Override - public void backupNow() throws RemoteException { + public void backupNowForUser(@UserIdInt int userId) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.backupNow(); + svc.backupNow(userId); } } @Override + public void backupNow() throws RemoteException { + backupNowForUser(binderGetCallingUserId()); + } + + @Override public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames) @@ -543,24 +564,36 @@ public class Trampoline extends IBackupManager.Stub { } @Override - public int requestBackup(String[] packages, IBackupObserver observer, - IBackupManagerMonitor monitor, int flags) throws RemoteException { + public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver + observer, IBackupManagerMonitor monitor, int flags) throws RemoteException { BackupManagerService svc = mService; if (svc == null) { return BackupManager.ERROR_BACKUP_NOT_ALLOWED; } - return svc.requestBackup(packages, observer, monitor, flags); + return svc.requestBackup(userId, packages, observer, monitor, flags); } @Override - public void cancelBackups() throws RemoteException { + public int requestBackup(String[] packages, IBackupObserver observer, + IBackupManagerMonitor monitor, int flags) throws RemoteException { + return requestBackupForUser(binderGetCallingUserId(), packages, + observer, monitor, flags); + } + + @Override + public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.cancelBackups(); + svc.cancelBackups(userId); } } @Override + public void cancelBackups() throws RemoteException { + cancelBackupsForUser(binderGetCallingUserId()); + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java index b01adc97eeae..96ef0ce45001 100644 --- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -22,7 +22,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.robolectric.Shadows.shadowOf; +import static org.testng.Assert.expectThrows; +import android.annotation.UserIdInt; import android.app.Application; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; @@ -32,11 +35,14 @@ import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import com.android.server.backup.testing.BackupManagerServiceTestUtils; import com.android.server.backup.testing.TransportData; +import com.android.server.testing.shadows.ShadowBinder; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,6 +50,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowContextWrapper; import java.io.File; import java.io.FileDescriptor; @@ -51,14 +59,19 @@ import java.io.PrintWriter; /** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */ @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBinder.class}) @Presubmit public class BackupManagerServiceTest { private static final String TEST_PACKAGE = "package"; private static final String TEST_TRANSPORT = "transport"; + private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1; + + private ShadowContextWrapper mShadowContext; @Mock private UserBackupManagerService mUserBackupManagerService; private BackupManagerService mBackupManagerService; private Context mContext; + @UserIdInt private int mUserId; /** Initialize {@link BackupManagerService}. */ @Before @@ -67,6 +80,8 @@ public class BackupManagerServiceTest { Application application = RuntimeEnvironment.application; mContext = application; + mShadowContext = shadowOf(application); + mUserId = NON_USER_SYSTEM; mBackupManagerService = new BackupManagerService( application, @@ -76,6 +91,15 @@ public class BackupManagerServiceTest { } /** + * Clean up and reset state that was created for testing {@link BackupManagerService} + * operations. + */ + @After + public void tearDown() throws Exception { + ShadowBinder.reset(); + } + + /** * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. * This is specifically to prevent overloading the logs in production. */ @@ -274,11 +298,41 @@ public class BackupManagerServiceTest { // --------------------------------------------- // Settings tests // --------------------------------------------- + /** + * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a + * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. + */ + @Test + public void setBackupEnabled_withoutPermission_throwsSecurityException() { + mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + expectThrows( + SecurityException.class, + () -> mBackupManagerService.setBackupEnabled(mUserId, true)); + } + + /** + * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not + * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is + * the same as the target user id. + */ + @Test + public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() { + ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId)); + mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + mBackupManagerService.setBackupEnabled(mUserId, true); + + verify(mUserBackupManagerService).setBackupEnabled(true); + } + /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception { - mBackupManagerService.setBackupEnabled(true); + mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + mBackupManagerService.setBackupEnabled(mUserId, true); verify(mUserBackupManagerService).setBackupEnabled(true); } @@ -299,10 +353,25 @@ public class BackupManagerServiceTest { verify(mUserBackupManagerService).setBackupProvisioned(true); } + /** + * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a + * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. + */ + @Test + public void testIsBackupEnabled_withoutPermission_throwsSecurityException() { + mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + expectThrows( + SecurityException.class, + () -> mBackupManagerService.isBackupEnabled(mUserId)); + } + /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception { - mBackupManagerService.isBackupEnabled(); + mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + mBackupManagerService.isBackupEnabled(mUserId); verify(mUserBackupManagerService).isBackupEnabled(); } @@ -330,30 +399,81 @@ public class BackupManagerServiceTest { verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages); } + /** + * Test verifying that {@link BackupManagerService#backupNow(int)} throws a + * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. + */ + @Test + public void testBackupNow_withoutPermission_throwsSecurityException() { + mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + expectThrows( + SecurityException.class, + () -> mBackupManagerService.backupNow(mUserId)); + } + /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testBackupNow_callsBackupNowForUser() throws Exception { - mBackupManagerService.backupNow(); + mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + mBackupManagerService.backupNow(mUserId); verify(mUserBackupManagerService).backupNow(); } + /** + * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver, + * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission. + */ + @Test + public void testRequestBackup_withoutPermission_throwsSecurityException() { + mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + String[] packages = {TEST_PACKAGE}; + IBackupObserver observer = mock(IBackupObserver.class); + IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); + + expectThrows( + SecurityException.class, + () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0)); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testRequestBackup_callsRequestBackupForUser() throws Exception { + mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); String[] packages = {TEST_PACKAGE}; IBackupObserver observer = mock(IBackupObserver.class); IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); - mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0); + mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, + /* flags */ 0); verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0); } + /** + * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a + * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission. + */ + @Test + public void testCancelBackups_withoutPermission_throwsSecurityException() { + mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + expectThrows( + SecurityException.class, + () -> mBackupManagerService.cancelBackups(mUserId)); + } + + /** Test that the backup service routes methods correctly to the user that requests it. */ @Test public void testCancelBackups_callsCancelBackupsForUser() throws Exception { - mBackupManagerService.cancelBackups(); + mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + + mBackupManagerService.cancelBackups(mUserId); verify(mUserBackupManagerService).cancelBackups(); } diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java index 1ece49ea2060..9de9f502d843 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java @@ -17,9 +17,11 @@ package com.android.server.testing.shadows; import android.os.Binder; +import android.os.UserHandle; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; /** * Extends {@link org.robolectric.shadows.ShadowBinder} with {@link Binder#clearCallingIdentity()} @@ -30,6 +32,7 @@ import org.robolectric.annotation.Implements; public class ShadowBinder extends org.robolectric.shadows.ShadowBinder { public static final Integer LOCAL_UID = 1000; private static Integer originalCallingUid; + private static UserHandle sCallingUserHandle; @Implementation protected static long clearCallingIdentity() { @@ -42,4 +45,30 @@ public class ShadowBinder extends org.robolectric.shadows.ShadowBinder { protected static void restoreCallingIdentity(long token) { setCallingUid(originalCallingUid); } + + public static void setCallingUserHandle(UserHandle userHandle) { + sCallingUserHandle = userHandle; + } + + /** + * Shadows {@link Binder#getCallingUserHandle()}. If {@link ShadowBinder#sCallingUserHandle} + * is set, return that; otherwise mimic the default implementation. + */ + @Implementation + public static UserHandle getCallingUserHandle() { + if (sCallingUserHandle != null) { + return sCallingUserHandle; + } else { + return UserHandle.of(UserHandle.getUserId(getCallingUid())); + } + } + + /** + * Clean up and reset state that was created for testing. + */ + @Resetter + public static void reset() { + sCallingUserHandle = null; + org.robolectric.shadows.ShadowBinder.reset(); + } } 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 fd010f1fed4f..751ed9b1bd15 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.annotation.UserIdInt; import android.app.backup.BackupManager; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; @@ -86,8 +87,9 @@ public class TrampolineTest { new ComponentName("package1", "class1"), new ComponentName("package2", "class2") }; - private final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1; + private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1; + @UserIdInt private int mUserId; @Mock private BackupManagerService mBackupManagerServiceMock; @Mock private Context mContextMock; @Mock private File mSuppressFileMock; @@ -110,11 +112,13 @@ public class TrampolineTest { TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock; TrampolineTestable.sSuppressFile = mSuppressFileMock; + TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM; TrampolineTestable.sCallingUid = Process.SYSTEM_UID; TrampolineTestable.sBackupDisabled = false; when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock); + mUserId = NON_USER_SYSTEM; mTrampoline = new TrampolineTestable(mContextMock); mContentResolver = new MockContentResolver(); @@ -361,10 +365,22 @@ public class TrampolineTest { } @Test - public void setBackupEnabled_forwarded() throws RemoteException { + public void setBackupEnabledForUser_forwarded() throws RemoteException { mTrampoline.initializeService(UserHandle.USER_SYSTEM); + + mTrampoline.setBackupEnabledForUser(mUserId, true); + + verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true); + } + + @Test + public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; + mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.setBackupEnabled(true); - verify(mBackupManagerServiceMock).setBackupEnabled(true); + + verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true); } @Test @@ -400,10 +416,22 @@ public class TrampolineTest { } @Test - public void isBackupEnabled_forwarded() throws RemoteException { + public void isBackupEnabledForUser_forwarded() throws RemoteException { + mTrampoline.initializeService(UserHandle.USER_SYSTEM); + + mTrampoline.isBackupEnabledForUser(mUserId); + + verify(mBackupManagerServiceMock).isBackupEnabled(mUserId); + } + + @Test + public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.isBackupEnabled(); - verify(mBackupManagerServiceMock).isBackupEnabled(); + + verify(mBackupManagerServiceMock).isBackupEnabled(mUserId); } @Test @@ -439,10 +467,22 @@ public class TrampolineTest { } @Test - public void backupNow_forwarded() throws RemoteException { + public void backupNowForUser_forwarded() throws RemoteException { mTrampoline.initializeService(UserHandle.USER_SYSTEM); + + mTrampoline.backupNowForUser(mUserId); + + verify(mBackupManagerServiceMock).backupNow(mUserId); + } + + @Test + public void backupNow_forwardedToCallingUserId() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; + mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.backupNow(); - verify(mBackupManagerServiceMock).backupNow(); + + verify(mBackupManagerServiceMock).backupNow(mUserId); } @Test @@ -798,15 +838,28 @@ public class TrampolineTest { } @Test - public void requestBackup_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.requestBackup(PACKAGE_NAMES, mBackupObserverMock, - mBackupManagerMonitorMock, 123)).thenReturn(456); + public void requestBackupForUser_forwarded() throws RemoteException { + when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES, + mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456); + mTrampoline.initializeService(UserHandle.USER_SYSTEM); + assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES, + mBackupObserverMock, mBackupManagerMonitorMock, 123)); + verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES, + mBackupObserverMock, mBackupManagerMonitorMock, 123); + } + + @Test + public void requestBackup_forwardedToCallingUserId() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; + when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES, + mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456); mTrampoline.initializeService(UserHandle.USER_SYSTEM); - assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES, mBackupObserverMock, - mBackupManagerMonitorMock, 123)); - verify(mBackupManagerServiceMock).requestBackup(PACKAGE_NAMES, mBackupObserverMock, - mBackupManagerMonitorMock, 123); + + assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES, + mBackupObserverMock, mBackupManagerMonitorMock, 123)); + verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES, + mBackupObserverMock, mBackupManagerMonitorMock, 123); } @Test @@ -816,10 +869,22 @@ public class TrampolineTest { } @Test - public void cancelBackups_forwarded() throws RemoteException { + public void cancelBackupsForUser_forwarded() throws RemoteException { mTrampoline.initializeService(UserHandle.USER_SYSTEM); + + mTrampoline.cancelBackupsForUser(mUserId); + + verify(mBackupManagerServiceMock).cancelBackups(mUserId); + } + + @Test + public void cancelBackups_forwardedToCallingUserId() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; + mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.cancelBackups(); - verify(mBackupManagerServiceMock).cancelBackups(); + + verify(mBackupManagerServiceMock).cancelBackups(mUserId); } @Test @@ -891,6 +956,7 @@ public class TrampolineTest { private static class TrampolineTestable extends Trampoline { static boolean sBackupDisabled = false; static File sSuppressFile = null; + static int sCallingUserId = -1; static int sCallingUid = -1; static BackupManagerService sBackupManagerServiceMock = null; private int mCreateServiceCallsCount = 0; @@ -909,6 +975,10 @@ public class TrampolineTest { return sSuppressFile; } + protected int binderGetCallingUserId() { + return sCallingUserId; + } + @Override protected int binderGetCallingUid() { return sCallingUid; |