summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChandan Nath <nathch@google.com>2018-12-07 16:49:37 +0000
committerChandan Nath <nathch@google.com>2018-12-11 17:06:30 +0000
commitcd44f758952f17f838f10381d24a9ff3e8b4cdbe (patch)
treeb64a72d55e1185bbad5b29292098374ea9983103
parenta96d8d0b78b93a428edad06b0202cb38741213ef (diff)
[Multi-user] Change BackupManager AIDL to accept userId in methods
Bug: 120120742 Test: 1) atest RunFrameworksServicesRoboTests 2) atest $(find \ frameworks/base/services/tests/servicestests/src/com/android/server/backup \ -name '*Test.java') 3) atest CtsBackupTestCases 4) atest CtsBackupHostTestCases 5) atest GtsBackupTestCases 6) atest GtsBackupHostTestCases 7) Toggle Backup/'Backup Now' in Settings 8) 'adb shell bmgr' enabled/backupnow flow Change-Id: I5dba38f6a24e07947d1b0948f9caefeca011205d
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java11
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl50
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java44
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java55
-rw-r--r--services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java130
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowBinder.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java102
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;