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

Commit 284f1bb4 authored by Christopher Tate's avatar Christopher Tate
Browse files

Can now restore a subset of apps from historical dataset

Adds the ability to filter a restore of an historical dataset so that it
only restores certain apps' data regardless of what is actually present
in the dataset.  This is currently only used by the bmgr command-line tool,
for debugging / developer support.

Bug 2021590

Change-Id: I7685e5d609b0f5506f71d70c26410602bb387659
parent 1d19c18f
Loading
Loading
Loading
Loading
+26 −6
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.app.backup.IRestoreSession;
import android.os.RemoteException;
import android.os.ServiceManager;

import java.util.HashSet;

public final class Bmgr {
    IBackupManager mBmgr;
    IRestoreSession mRestore;
@@ -45,7 +47,6 @@ public final class Bmgr {
    }

    public void run(String[] args) {
        boolean validCommand = false;
        if (args.length < 1) {
            showUsage();
            return;
@@ -329,7 +330,13 @@ public final class Bmgr {
        } else {
            try {
                long token = Long.parseLong(arg, 16);
                doRestoreAll(token);
                HashSet<String> filter = null;
                while ((arg = nextArg()) != null) {
                    if (filter == null) filter = new HashSet<String>();
                    filter.add(arg);
                }

                doRestoreAll(token, filter);
            } catch (NumberFormatException e) {
                showUsage();
                return;
@@ -364,7 +371,7 @@ public final class Bmgr {
        }
    }

    private void doRestoreAll(long token) {
    private void doRestoreAll(long token, HashSet<String> filter) {
        RestoreObserver observer = new RestoreObserver();

        try {
@@ -383,7 +390,13 @@ public final class Bmgr {
                    for (RestoreSet s : sets) {
                        if (s.token == token) {
                            System.out.println("Scheduling restore: " + s.name);
                            if (filter == null) {
                                didRestore = (mRestore.restoreAll(token, observer) == 0);
                            } else {
                                String[] names = new String[filter.size()];
                                filter.toArray(names);
                                didRestore = (mRestore.restoreSome(token, observer, names) == 0);
                            }
                            break;
                        }
                    }
@@ -430,6 +443,7 @@ public final class Bmgr {
        System.err.println("       bmgr list sets");
        System.err.println("       bmgr transport WHICH");
        System.err.println("       bmgr restore TOKEN");
        System.err.println("       bmgr restore TOKEN PACKAGE...");
        System.err.println("       bmgr restore PACKAGE");
        System.err.println("       bmgr run");
        System.err.println("       bmgr wipe PACKAGE");
@@ -457,12 +471,18 @@ public final class Bmgr {
        System.err.println("The 'transport' command designates the named transport as the currently");
        System.err.println("active one.  This setting is persistent across reboots.");
        System.err.println("");
        System.err.println("The 'restore' command when given a restore token initiates a full-system");
        System.err.println("The 'restore' command when given just a restore token initiates a full-system");
        System.err.println("restore operation from the currently active transport.  It will deliver");
        System.err.println("the restore set designated by the TOKEN argument to each application");
        System.err.println("that had contributed data to that restore set.");
        System.err.println("");
        System.err.println("The 'restore' command when given a package name intiates a restore of");
        System.err.println("The 'restore' command when given a token and one or more package names");
        System.err.println("initiates a restore operation of just those given packages from the restore");
        System.err.println("set designated by the TOKEN argument.  It is effectively the same as the");
        System.err.println("'restore' operation supplying only a token, but applies a filter to the");
        System.err.println("set of applications to be restored.");
        System.err.println("");
        System.err.println("The 'restore' command when given just a package name intiates a restore of");
        System.err.println("just that one package according to the restore set selection algorithm");
        System.err.println("used by the RestoreSession.restorePackage() method.");
        System.err.println("");
+19 −0
Original line number Diff line number Diff line
@@ -51,6 +51,25 @@ interface IRestoreSession {
     */
    int restoreAll(long token, IRestoreObserver observer);

    /**
     * Restore select packages from the given set onto the device, replacing the
     * current data of any app contained in the set with the data previously
     * backed up.
     *
     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
     *
     * @return Zero on success, nonzero on error. The observer will only receive
     *   progress callbacks if this method returned zero.
     * @param token The token from {@link getAvailableRestoreSets()} corresponding to
     *   the restore set that should be used.
     * @param observer If non-null, this binder points to an object that will receive
     *   progress callbacks during the restore operation.
     * @param packages The set of packages for which to attempt a restore.  Regardless of
     *   the contents of the actual back-end dataset named by {@code token}, only
     *   applications mentioned in this list will have their data restored.
     */
    int restoreSome(long token, IRestoreObserver observer, in String[] packages);

    /**
     * Restore a single application from backup.  The data will be restored from the
     * current backup dataset if the given package has stored data there, or from
+34 −0
Original line number Diff line number Diff line
@@ -86,6 +86,40 @@ public class RestoreSession {
        return err;
    }

    /**
     * Restore select packages from the given set onto the device, replacing the
     * current data of any app contained in the set with the data previously
     * backed up.
     *
     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
     *
     * @return Zero on success, nonzero on error. The observer will only receive
     *   progress callbacks if this method returned zero.
     * @param token The token from {@link getAvailableRestoreSets()} corresponding to
     *   the restore set that should be used.
     * @param observer If non-null, this binder points to an object that will receive
     *   progress callbacks during the restore operation.
     * @param packages The set of packages for which to attempt a restore.  Regardless of
     *   the contents of the actual back-end dataset named by {@code token}, only
     *   applications mentioned in this list will have their data restored.
     *
     * @hide
     */
    public int restoreSome(long token, RestoreObserver observer, String[] packages) {
        int err = -1;
        if (mObserver != null) {
            Log.d(TAG, "restoreAll() called during active restore");
            return -1;
        }
        mObserver = new RestoreObserverWrapper(mContext, observer);
        try {
            err = mBinder.restoreSome(token, mObserver, packages);
        } catch (RemoteException e) {
            Log.d(TAG, "Can't contact server to restore packages");
        }
        return err;
    }

    /**
     * Restore a single application from backup.  The data will be restored from the
     * current backup dataset if the given package has stored data there, or from
+105 −3
Original line number Diff line number Diff line
@@ -224,6 +224,7 @@ class BackupManagerService extends IBackupManager.Stub {
        public PackageInfo pkgInfo;
        public int pmToken; // in post-install restore, the PM's token for this transaction
        public boolean needFullBackup;
        public String[] filterSet;

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
                long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
@@ -233,6 +234,7 @@ class BackupManagerService extends IBackupManager.Stub {
            pkgInfo = _pkg;
            pmToken = _pmToken;
            needFullBackup = _needFullBackup;
            filterSet = null;
        }

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
@@ -243,6 +245,18 @@ class BackupManagerService extends IBackupManager.Stub {
            pkgInfo = null;
            pmToken = 0;
            needFullBackup = _needFullBackup;
            filterSet = null;
        }

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
                String[] _filterSet, boolean _needFullBackup) {
            transport = _transport;
            observer = _obs;
            token = _token;
            pkgInfo = null;
            pmToken = 0;
            needFullBackup = _needFullBackup;
            filterSet = _filterSet;
        }
    }

@@ -404,7 +418,7 @@ class BackupManagerService extends IBackupManager.Stub {
                Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
                (new PerformRestoreTask(params.transport, params.observer,
                        params.token, params.pkgInfo, params.pmToken,
                        params.needFullBackup)).run();
                        params.needFullBackup, params.filterSet)).run();
                break;
            }

@@ -3020,6 +3034,7 @@ class BackupManagerService extends IBackupManager.Stub {
        private File mStateDir;
        private int mPmToken;
        private boolean mNeedFullBackup;
        private HashSet<String> mFilterSet;

        class RestoreRequest {
            public PackageInfo app;
@@ -3033,7 +3048,7 @@ class BackupManagerService extends IBackupManager.Stub {

        PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
                long restoreSetToken, PackageInfo targetPackage, int pmToken,
                boolean needFullBackup) {
                boolean needFullBackup, String[] filterSet) {
            mTransport = transport;
            mObserver = observer;
            mToken = restoreSetToken;
@@ -3041,6 +3056,15 @@ class BackupManagerService extends IBackupManager.Stub {
            mPmToken = pmToken;
            mNeedFullBackup = needFullBackup;

            if (filterSet != null) {
                mFilterSet = new HashSet<String>();
                for (String pkg : filterSet) {
                    mFilterSet.add(pkg);
                }
            } else {
                mFilterSet = null;
            }

            try {
                mStateDir = new File(mBaseStateDir, transport.transportDirName());
            } catch (RemoteException e) {
@@ -3052,7 +3076,8 @@ class BackupManagerService extends IBackupManager.Stub {
            long startRealtime = SystemClock.elapsedRealtime();
            if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
                    + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
                    + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
                    + " mTargetPackage=" + mTargetPackage + " mFilterSet=" + mFilterSet
                    + " mPmToken=" + mPmToken);

            PackageManagerBackupAgent pmAgent = null;
            int error = -1; // assume error
@@ -3071,6 +3096,22 @@ class BackupManagerService extends IBackupManager.Stub {

                List<PackageInfo> agentPackages = allAgentPackages();
                if (mTargetPackage == null) {
                    // if there's a filter set, strip out anything that isn't
                    // present before proceeding
                    if (mFilterSet != null) {
                        for (int i = agentPackages.size() - 1; i >= 0; i--) {
                            final PackageInfo pkg = agentPackages.get(i);
                            if (! mFilterSet.contains(pkg.packageName)) {
                                agentPackages.remove(i);
                            }
                        }
                        if (DEBUG) {
                            Slog.i(TAG, "Post-filter package set for restore:");
                            for (PackageInfo p : agentPackages) {
                                Slog.i(TAG, "    " + p);
                            }
                        }
                    }
                    restorePackages.addAll(agentPackages);
                } else {
                    // Just one package to attempt restore of
@@ -4266,6 +4307,67 @@ class BackupManagerService extends IBackupManager.Stub {
            return -1;
        }

        public synchronized int restoreSome(long token, IRestoreObserver observer,
                String[] packages) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                    "performRestore");

            if (DEBUG) {
                StringBuilder b = new StringBuilder(128);
                b.append("restoreSome token=");
                b.append(Long.toHexString(token));
                b.append(" observer=");
                b.append(observer.toString());
                b.append(" packages=");
                if (packages == null) {
                    b.append("null");
                } else {
                    b.append('{');
                    boolean first = true;
                    for (String s : packages) {
                        if (!first) {
                            b.append(", ");
                        } else first = false;
                        b.append(s);
                    }
                    b.append('}');
                }
                Slog.d(TAG, b.toString());
            }

            if (mEnded) {
                throw new IllegalStateException("Restore session already ended");
            }

            if (mRestoreTransport == null || mRestoreSets == null) {
                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
                return -1;
            }

            if (mPackageName != null) {
                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
                return -1;
            }

            synchronized (mQueueLock) {
                for (int i = 0; i < mRestoreSets.length; i++) {
                    if (token == mRestoreSets[i].token) {
                        long oldId = Binder.clearCallingIdentity();
                        mWakelock.acquire();
                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                        msg.obj = new RestoreParams(mRestoreTransport, observer, token,
                                packages, true);
                        mBackupHandler.sendMessage(msg);
                        Binder.restoreCallingIdentity(oldId);
                        return 0;
                    }
                }
            }

            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
            return -1;
        }

        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);