Loading cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +6 −5 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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); Loading core/java/android/app/backup/IBackupManager.aidl +50 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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. * Loading @@ -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(); } services/backup/java/com/android/server/backup/BackupManagerService.java +37 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -79,6 +82,7 @@ public class BackupManagerService { return sInstance; } private final Context mContext; private UserBackupManagerService mUserBackupManagerService; /** Instantiate a new instance of {@link BackupManagerService}. */ Loading @@ -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) { Loading Loading @@ -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); } Loading @@ -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(); } Loading @@ -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(); } Loading @@ -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(); } Loading Loading @@ -533,7 +561,9 @@ 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: " Slog.e( TAG, "Unable to record backup enable state; reverting to disabled: " + e.getMessage()); enableFile.delete(); stage.delete(); Loading services/backup/java/com/android/server/backup/Trampoline.java +44 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -319,13 +324,19 @@ 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; Loading @@ -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 Loading @@ -361,11 +377,16 @@ 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 Loading Loading @@ -543,21 +564,33 @@ 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 Loading services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java +125 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -67,6 +80,8 @@ public class BackupManagerServiceTest { Application application = RuntimeEnvironment.application; mContext = application; mShadowContext = shadowOf(application); mUserId = NON_USER_SYSTEM; mBackupManagerService = new BackupManagerService( application, Loading @@ -75,6 +90,15 @@ public class BackupManagerServiceTest { mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService); } /** * 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. Loading Loading @@ -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); } Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading
cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +6 −5 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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); Loading
core/java/android/app/backup/IBackupManager.aidl +50 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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. * Loading @@ -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(); }
services/backup/java/com/android/server/backup/BackupManagerService.java +37 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -79,6 +82,7 @@ public class BackupManagerService { return sInstance; } private final Context mContext; private UserBackupManagerService mUserBackupManagerService; /** Instantiate a new instance of {@link BackupManagerService}. */ Loading @@ -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) { Loading Loading @@ -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); } Loading @@ -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(); } Loading @@ -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(); } Loading @@ -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(); } Loading Loading @@ -533,7 +561,9 @@ 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: " Slog.e( TAG, "Unable to record backup enable state; reverting to disabled: " + e.getMessage()); enableFile.delete(); stage.delete(); Loading
services/backup/java/com/android/server/backup/Trampoline.java +44 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -319,13 +324,19 @@ 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; Loading @@ -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 Loading @@ -361,11 +377,16 @@ 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 Loading Loading @@ -543,21 +564,33 @@ 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 Loading
services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java +125 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -67,6 +80,8 @@ public class BackupManagerServiceTest { Application application = RuntimeEnvironment.application; mContext = application; mShadowContext = shadowOf(application); mUserId = NON_USER_SYSTEM; mBackupManagerService = new BackupManagerService( application, Loading @@ -75,6 +90,15 @@ public class BackupManagerServiceTest { mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService); } /** * 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. Loading Loading @@ -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); } Loading @@ -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(); } Loading Loading @@ -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(); } Loading