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

Commit a2fe6c5b authored by Ruslan Tkhakokhov's avatar Ruslan Tkhakokhov
Browse files

[Multi-user] Add -user param to adb backup/restore

Add an optional parameter -user to provide ID of the user for which to
run backup/restore operation. Add robolectric test to verify the
new parameter is proccessed correctly.

Bug: 119908153
Test: 1) atest BackupTest
      2) atest BackupManagerServiceTest
      3) atest TrampolineTest
      4) atest GtsBackupTestCases
      5) atest GtsBackupHostTestCases
      6) Manual:
        - Run "adb backup -all" and verify that backup is successfull
        - Run "adb restore" and verify that restore is successfull
        - Run "adb backup -all -user 10" and verify that backup faield as
          it's only currently supported for system user
        - Run "adb restore -user 10" and verify that restore failed as it's
          only currently supported for system user

Change-Id: I6dbf9c87eedd5a72da0446beff7d2551f98f2654
parent 1982ca78
Loading
Loading
Loading
Loading
+58 −14
Original line number Diff line number Diff line
@@ -16,13 +16,17 @@

package com.android.commands.bu;

import android.annotation.UserIdInt;
import android.app.backup.IBackupManager;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.system.OsConstants;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

import java.io.IOException;
import java.util.ArrayList;

@@ -33,35 +37,50 @@ public final class Backup {
    int mNextArg;
    IBackupManager mBackupManager;

    @VisibleForTesting
    Backup(IBackupManager backupManager) {
        mBackupManager = backupManager;
    }

    Backup() {
        mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
    }

    public static void main(String[] args) {
        Log.d(TAG, "Beginning: " + args[0]);
        mArgs = args;
        try {
            new Backup().run();
            new Backup().run(args);
        } catch (Exception e) {
            Log.e(TAG, "Error running backup/restore", e);
        }
        Log.d(TAG, "Finished.");
    }

    public void run() {
        mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
    public void run(String[] args) {
        if (mBackupManager == null) {
            Log.e(TAG, "Can't obtain Backup Manager binder");
            return;
        }

        Log.d(TAG, "Beginning: " + args[0]);
        mArgs = args;

        int userId = parseUserId();
        if (!isBackupActiveForUser(userId)) {
            Log.e(TAG, "BackupManager is not available for user " + userId);
            return;
        }

        String arg = nextArg();
        if (arg.equals("backup")) {
            doBackup(OsConstants.STDOUT_FILENO);
            doBackup(OsConstants.STDOUT_FILENO, userId);
        } else if (arg.equals("restore")) {
            doRestore(OsConstants.STDIN_FILENO);
            doRestore(OsConstants.STDIN_FILENO, userId);
        } else {
            showUsage();
        }
    }

    private void doBackup(int socketFd) {
    private void doBackup(int socketFd, @UserIdInt int userId) {
        ArrayList<String> packages = new ArrayList<String>();
        boolean saveApks = false;
        boolean saveObbs = false;
@@ -105,6 +124,10 @@ public final class Backup {
                    doKeyValue = true;
                } else if ("-nokeyvalue".equals(arg)) {
                    doKeyValue = false;
                } else if ("-user".equals(arg)) {
                    // User ID has been processed in run(), ignore the next argument.
                    nextArg();
                    continue;
                } else {
                    Log.w(TAG, "Unknown backup flag " + arg);
                    continue;
@@ -128,7 +151,7 @@ public final class Backup {
        try {
            fd = ParcelFileDescriptor.adoptFd(socketFd);
            String[] packArray = new String[packages.size()];
            mBackupManager.adbBackup(fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
            mBackupManager.adbBackup(userId, fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
                    allIncludesSystem, doCompress, doKeyValue, packages.toArray(packArray));
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to invoke backup manager for backup");
@@ -143,12 +166,12 @@ public final class Backup {
        }
    }

    private void doRestore(int socketFd) {
    private void doRestore(int socketFd, @UserIdInt int userId) {
        // No arguments to restore
        ParcelFileDescriptor fd = null;
        try {
            fd = ParcelFileDescriptor.adoptFd(socketFd);
            mBackupManager.adbRestore(fd);
            mBackupManager.adbRestore(userId, fd);
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to invoke backup manager for restore");
        } finally {
@@ -160,11 +183,31 @@ public final class Backup {
        }
    }

    private @UserIdInt int parseUserId() {
        for (int argNumber = 0; argNumber < mArgs.length - 1; argNumber++) {
            if ("-user".equals(mArgs[argNumber])) {
                return UserHandle.parseUserArg(mArgs[argNumber + 1]);
            }
        }

        return UserHandle.USER_SYSTEM;
    }

    private boolean isBackupActiveForUser(int userId) {
        try {
            return mBackupManager.isBackupServiceActive(userId);
        } catch (RemoteException e) {
            Log.e(TAG, "Could not access BackupManager: " + e.toString());
            return false;
        }
    }

    private static void showUsage() {
        System.err.println(" backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all]");
        System.err.println("        [-system|-nosystem] [-keyvalue|-nokeyvalue] [PACKAGE...]");
        System.err.println(" backup [-user USER_ID] [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared]");
        System.err.println("        [-all] [-system|-nosystem] [-keyvalue|-nokeyvalue] [PACKAGE...]");
        System.err.println("     write an archive of the device's data to FILE [default=backup.adb]");
        System.err.println("     package list optional if -all/-shared are supplied");
        System.err.println("     -user: user ID for which to perform the operation (default - system user)");
        System.err.println("     -apk/-noapk: do/don't back up .apk files (default -noapk)");
        System.err.println("     -obb/-noobb: do/don't back up .obb files (default -noobb)");
        System.err.println("     -shared|-noshared: do/don't back up shared storage (default -noshared)");
@@ -172,7 +215,8 @@ public final class Backup {
        System.err.println("     -system|-nosystem: include system apps in -all (default -system)");
        System.err.println("     -keyvalue|-nokeyvalue: include apps that perform key/value backups.");
        System.err.println("         (default -nokeyvalue)");
        System.err.println(" restore FILE             restore device contents from FILE");
        System.err.println(" restore [-user USER_ID] FILE       restore device contents from FILE");
        System.err.println("     -user: user ID for which to perform the operation (default - system user)");
    }

    private String nextArg() {
+10 −3
Original line number Diff line number Diff line
@@ -189,8 +189,11 @@ interface IBackupManager {
     * completed.
     *
     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
     * If the {@code userId} is different from the calling user id, then the caller must hold the
     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
     *
     * @param fd The file descriptor to which a 'tar' file stream is to be written
     * @param userId User id for which backup should be performed.
     * @param fd The file descriptor to which a 'tar' file stream is to be written.
     * @param includeApks If <code>true</code>, the resulting tar stream will include the
     *     application .apk files themselves as well as their data.
     * @param includeObbs If <code>true</code>, the resulting tar stream will include any
@@ -209,7 +212,7 @@ interface IBackupManager {
     * @param packageNames The package names of the apps whose data (and optionally .apk files)
     *     are to be backed up.  The <code>allApps</code> parameter supersedes this.
     */
    void adbBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
    void adbBackup(int userId, in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
            boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem,
            boolean doCompress, boolean doKeyValue, in String[] packageNames);

@@ -227,8 +230,12 @@ interface IBackupManager {
     * Currently only used by the 'adb restore' command.
     *
     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
     * If the {@code userId} is different from the calling user id, then the caller must hold the
     * android.permission.INTERACT_ACROSS_USERS_FULL.
     *
     * @param userId User id for which restore should be performed.
     */
    void adbRestore(in ParcelFileDescriptor fd);
    void adbRestore(int userId, in ParcelFileDescriptor fd);

    /**
     * Confirm that the requested full backup/restore operation can proceed.  The system will
+6 −1
Original line number Diff line number Diff line
@@ -477,6 +477,7 @@ public class BackupManagerService {
     * requires on-screen confirmation by the user.
     */
    public void adbBackup(
            @UserIdInt int userId,
            ParcelFileDescriptor fd,
            boolean includeApks,
            boolean includeObbs,
@@ -487,6 +488,8 @@ public class BackupManagerService {
            boolean doCompress,
            boolean doKeyValue,
            String[] packageNames) {
        enforceCallingPermissionOnUserId(userId, "adbBackup");

        mUserBackupManagerService.adbBackup(
                fd,
                includeApks,
@@ -505,7 +508,9 @@ public class BackupManagerService {
     * is synchronous and does not return to the caller until the restore has been completed. It
     * requires on-screen confirmation by the user.
     */
    public void adbRestore(ParcelFileDescriptor fd) {
    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
        enforceCallingPermissionOnUserId(userId, "setBackupEnabled");

        mUserBackupManagerService.adbRestore(fd);
    }

+7 −8
Original line number Diff line number Diff line
@@ -389,14 +389,13 @@ public class Trampoline extends IBackupManager.Stub {
        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)
                    throws RemoteException {
    public void adbBackup(@UserIdInt int userId, ParcelFileDescriptor fd,
            boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
            boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue,
            String[] packageNames) throws RemoteException {
        BackupManagerService svc = mService;
        if (svc != null) {
            svc.adbBackup(fd, includeApks, includeObbs, includeShared, doWidgets,
            svc.adbBackup(userId, fd, includeApks, includeObbs, includeShared, doWidgets,
                    allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
        }
    }
@@ -410,10 +409,10 @@ public class Trampoline extends IBackupManager.Stub {
    }

    @Override
    public void adbRestore(ParcelFileDescriptor fd) throws RemoteException {
    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) throws RemoteException {
        BackupManagerService svc = mService;
        if (svc != null) {
            svc.adbRestore(fd);
            svc.adbRestore(userId, fd);
        }
    }

+4 −3
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AppGlobals;
@@ -2423,9 +2424,9 @@ public class UserBackupManagerService {
     * return to the caller until the backup has been completed. It requires on-screen confirmation
     * by the user.
     */
    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
            boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
            boolean compress, boolean doKeyValue, String[] pkgList) {
    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks,
            boolean includeObbs, boolean includeShared, boolean doWidgets, boolean doAllApps,
            boolean includeSystem, boolean compress, boolean doKeyValue, String[] pkgList) {
        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");

        final int callingUserHandle = UserHandle.getCallingUserId();
Loading