Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4815ebf authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[Multi-user] Change BackupManager AIDL to accept userId in methods"

parents b867b4c4 cd44f758
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -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);
+50 −0
Original line number Diff line number Diff line
@@ -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();
}
+37 −7
Original line number Diff line number Diff line
@@ -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,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();
+44 −11
Original line number Diff line number Diff line
@@ -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,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;
@@ -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,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
@@ -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
+125 −5
Original line number Diff line number Diff line
@@ -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,
@@ -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.
@@ -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();
    }
Loading