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

Commit 5c1779b8 authored by Chris Tate's avatar Chris Tate Committed by Android (Google) Code Review
Browse files

Merge "Add single-package restore from an app's most-recent data"

parents 64226a49 8472581a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -315,7 +315,7 @@ public final class Bmgr {
                for (RestoreSet s : sets) {
                    if (s.token == token) {
                        System.out.println("Scheduling restore: " + s.name);
                        didRestore = (mRestore.performRestore(token, observer) == 0);
                        didRestore = (mRestore.restoreAll(token, observer) == 0);
                        break;
                    }
                }
+20 −1
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ interface IRestoreSession {
     * Restore the given set onto the device, replacing the current data of any app
     * contained in the restore 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
@@ -47,7 +49,24 @@ interface IRestoreSession {
     * @param observer If non-null, this binder points to an object that will receive
     *   progress callbacks during the restore operation.
     */
    int performRestore(long token, IRestoreObserver observer);
    int restoreAll(long token, IRestoreObserver observer);

    /**
     * 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
     * the dataset used during the last full device setup operation if the current
     * backup dataset has no matching data.  If no backup data exists for this package
     * in either source, a nonzero value will be returned.
     *
     * @return Zero on success; nonzero on error.  The observer will only receive
     *   progress callbacks if this method returned zero.
     * @param packageName The name of the package whose data to restore.  If this is
     *   not the name of the caller's own package, then the android.permission.BACKUP
     *   permission must be held.
     * @param observer If non-null, this binder points to an object that will receive
     *   progress callbacks during the restore operation.
     */
    int restorePackage(in String packageName, IRestoreObserver observer);

    /**
     * End this restore session.  After this method is called, the IRestoreSession binder
+40 −9
Original line number Diff line number Diff line
@@ -58,25 +58,56 @@ public class RestoreSession {
     * Restore the given set onto the device, replacing the current data of any app
     * contained in the restore 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
     * @param token The token from {@link getAvailableRestoreSets()} corresponding to
     *   the restore set that should be used.
     * @param observer If non-null, this argument points to an object that will receive
     *   progress callbacks during the restore operation. These callbacks will occur
     *   on the main thread of the application.
     * @param observer If non-null, this binder points to an object that will receive
     *   progress callbacks during the restore operation.
     */
    public int restoreAll(long token, RestoreObserver observer) {
        int err = -1;
        if (mObserver != null) {
            Log.d(TAG, "restoreAll() called during active restore");
            return -1;
        }
        mObserver = new RestoreObserverWrapper(mContext, observer);
        try {
            err = mBinder.restoreAll(token, mObserver);
        } catch (RemoteException e) {
            Log.d(TAG, "Can't contact server to restore");
        }
        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
     * the dataset used during the last full device setup operation if the current
     * backup dataset has no matching data.  If no backup data exists for this package
     * in either source, a nonzero value will be returned.
     *
     * @return Zero on success; nonzero on error.  The observer will only receive
     *   progress callbacks if this method returned zero.
     * @param packageName The name of the package whose data to restore.  If this is
     *   not the name of the caller's own package, then the android.permission.BACKUP
     *   permission must be held.
     * @param observer If non-null, this binder points to an object that will receive
     *   progress callbacks during the restore operation.
     */
    public int performRestore(long token, RestoreObserver observer) {
    public int restorePackage(String packageName, RestoreObserver observer) {
        int err = -1;
        if (mObserver != null) {
            Log.d(TAG, "performRestore() called during active restore");
            Log.d(TAG, "restorePackage() called during active restore");
            return -1;
        }
        mObserver = new RestoreObserverWrapper(mContext, observer);
        try {
            err = mBinder.performRestore(token, mObserver);
            err = mBinder.restorePackage(packageName, mObserver);
        } catch (RemoteException e) {
            Log.d(TAG, "Can't contact server to perform restore");
            Log.d(TAG, "Can't contact server to restore package");
        }
        return err;
    }
@@ -87,7 +118,7 @@ public class RestoreSession {
     *
     * <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
     *   even if {@link #getAvailableRestoreSets()} or
     *   {@link #performRestore(long, RestoreObserver)} failed.
     *   {@link #restorePackage(long, String, RestoreObserver)} failed.
     */
    public void endRestoreSession() {
        try {
+123 −9
Original line number Diff line number Diff line
@@ -176,11 +176,21 @@ class BackupManagerService extends IBackupManager.Stub {
        public IBackupTransport transport;
        public IRestoreObserver observer;
        public long token;
        public PackageInfo pkgInfo;

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
                long _token, PackageInfo _pkg) {
            transport = _transport;
            observer = _obs;
            token = _token;
            pkgInfo = _pkg;
        }

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
            transport = _transport;
            observer = _obs;
            token = _token;
            pkgInfo = null;
        }
    }

@@ -210,10 +220,16 @@ class BackupManagerService extends IBackupManager.Stub {
    File mJournalDir;
    File mJournal;

    // Keep a log of all the apps we've ever backed up
    // Keep a log of all the apps we've ever backed up, and what the
    // dataset tokens are for both the current backup dataset and
    // the ancestral dataset.
    private File mEverStored;
    HashSet<String> mEverStoredApps = new HashSet<String>();

    File mTokenFile;
    long mAncestralToken = 0;
    long mCurrentToken = 0;

    // Persistently track the need to do a full init
    static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
    HashSet<String> mPendingInits = new HashSet<String>();  // transport names
@@ -277,7 +293,7 @@ class BackupManagerService extends IBackupManager.Stub {
                RestoreParams params = (RestoreParams)msg.obj;
                Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
                (new PerformRestoreTask(params.transport, params.observer,
                        params.token)).run();
                        params.token, params.pkgInfo)).run();
                break;
            }

@@ -475,6 +491,16 @@ class BackupManagerService extends IBackupManager.Stub {
    private void initPackageTracking() {
        if (DEBUG) Log.v(TAG, "Initializing package tracking");

        // Remember our ancestral dataset
        mTokenFile = new File(mBaseStateDir, "ancestral");
        try {
            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
            mAncestralToken = tf.readLong();
            mCurrentToken = tf.readLong();
        } catch (IOException e) {
            Log.w(TAG, "Unable to read token file", e);
        }

        // Keep a log of what apps we've ever backed up.  Because we might have
        // rebooted in the middle of an operation that was removing something from
        // this log, we sanity-check its contents here and reconstruct it.
@@ -607,6 +633,9 @@ class BackupManagerService extends IBackupManager.Stub {
            mEverStoredApps.clear();
            mEverStored.delete();

            mCurrentToken = 0;
            writeRestoreTokens();

            // Remove all the state files
            for (File sf : stateFileDir.listFiles()) {
                // ... but don't touch the needs-init sentinel
@@ -880,7 +909,7 @@ class BackupManagerService extends IBackupManager.Stub {
        addPackageParticipantsLockedInner(packageName, allApps);
    }

    // Called from the backup thread: record that the given app has been successfully
    // Called from the backup task: record that the given app has been successfully
    // backed up at least once
    void logBackupComplete(String packageName) {
        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
@@ -938,6 +967,18 @@ class BackupManagerService extends IBackupManager.Stub {
        }
    }

    // Record the current and ancestral backup tokens persistently
    void writeRestoreTokens() {
        try {
            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
            af.writeLong(mAncestralToken);
            af.writeLong(mCurrentToken);
            af.close();
        } catch (IOException e) {
            Log.w(TAG, "Unable to write token file:", e);
        }
    }

    // Return the given transport
    private IBackupTransport getTransport(String transportName) {
        synchronized (mTransports) {
@@ -1154,6 +1195,16 @@ class BackupManagerService extends IBackupManager.Stub {
                Log.e(TAG, "Error in backup thread", e);
                status = BackupConstants.TRANSPORT_ERROR;
            } finally {
                // If everything actually went through and this is the first time we've
                // done a backup, we can now record what the current backup dataset token
                // is.
                if ((mCurrentToken == 0) && (status != BackupConstants.TRANSPORT_OK)) {
                    try {
                        mCurrentToken = mTransport.getCurrentRestoreSet();
                    } catch (RemoteException e) { /* cannot happen */ }
                    writeRestoreTokens();
                }

                // If things went wrong, we need to re-stage the apps we had expected
                // to be backing up in this pass.  This journals the package names in
                // the current active pending-backup file, not in the we are holding
@@ -1395,6 +1446,7 @@ class BackupManagerService extends IBackupManager.Stub {
        private IBackupTransport mTransport;
        private IRestoreObserver mObserver;
        private long mToken;
        private PackageInfo mTargetPackage;
        private File mStateDir;

        class RestoreRequest {
@@ -1408,11 +1460,11 @@ class BackupManagerService extends IBackupManager.Stub {
        }

        PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
                long restoreSetToken) {
                long restoreSetToken, PackageInfo targetPackage) {
            mTransport = transport;
            Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
            mObserver = observer;
            mToken = restoreSetToken;
            mTargetPackage = targetPackage;

            try {
                mStateDir = new File(mBaseStateDir, transport.transportDirName());
@@ -1424,7 +1476,8 @@ class BackupManagerService extends IBackupManager.Stub {
        public void run() {
            long startRealtime = SystemClock.elapsedRealtime();
            if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
                    + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken));
                    + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
                    + " mTargetPackage=" + mTargetPackage);
            /**
             * Restore sequence:
             *
@@ -1441,7 +1494,7 @@ class BackupManagerService extends IBackupManager.Stub {
             *
             * On errors, we try our best to recover and move on to the next
             * application, but if necessary we abort the whole operation --
             * the user is waiting, after al.
             * the user is waiting, after all.
             */

            int error = -1; // assume error
@@ -1459,7 +1512,12 @@ class BackupManagerService extends IBackupManager.Stub {
                restorePackages.add(omPackage);

                List<PackageInfo> agentPackages = allAgentPackages();
                if (mTargetPackage == null) {
                    restorePackages.addAll(agentPackages);
                } else {
                    // Just one package to attempt restore of
                    restorePackages.add(mTargetPackage);
                }

                // let the observer know that we're running
                if (mObserver != null) {
@@ -1641,6 +1699,13 @@ class BackupManagerService extends IBackupManager.Stub {
                    }
                }

                // If this was a restoreAll operation, record that this was our
                // ancestral dataset
                if (mTargetPackage == null) {
                    mAncestralToken = mToken;
                    writeRestoreTokens();
                }

                // done; we can finally release the wakelock
                mWakelock.release();
            }
@@ -2219,7 +2284,7 @@ class BackupManagerService extends IBackupManager.Stub {
            }
        }

        public synchronized int performRestore(long token, IRestoreObserver observer) {
        public synchronized int restoreAll(long token, IRestoreObserver observer) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                    "performRestore");

@@ -2249,6 +2314,55 @@ class BackupManagerService extends IBackupManager.Stub {
            return -1;
        }

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

            PackageInfo app = null;
            try {
                app = mPackageManager.getPackageInfo(packageName, 0);
            } catch (NameNotFoundException nnf) {
                Log.w(TAG, "Asked to restore nonexistent pkg " + packageName);
                return -1;
            }

            // If the caller is not privileged and is not coming from the target
            // app's uid, throw a permission exception back to the caller.
            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
                    Binder.getCallingPid(), Binder.getCallingUid());
            if ((perm == PackageManager.PERMISSION_DENIED) &&
                    (app.applicationInfo.uid != Binder.getCallingUid())) {
                Log.w(TAG, "restorePackage: bad packageName=" + packageName
                        + " or calling uid=" + Binder.getCallingUid());
                throw new SecurityException("No permission to restore other packages");
            }

            // So far so good; we're allowed to try to restore this package.  Now
            // check whether there is data for it in the current dataset, falling back
            // to the ancestral dataset if not.
            long token = mAncestralToken;
            synchronized (mQueueLock) {
                if (mEverStoredApps.contains(packageName)) {
                    token = mCurrentToken;
                }
            }

            // If we didn't come up with a place to look -- no ancestral dataset and
            // the app has never been backed up from this device -- there's nothing
            // to do but return failure.
            if (token == 0) {
                return -1;
            }

            // Ready to go:  enqueue the restore request and claim success
            long oldId = Binder.clearCallingIdentity();
            mWakelock.acquire();
            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
            msg.obj = new RestoreParams(mRestoreTransport, observer, token, app);
            mBackupHandler.sendMessage(msg);
            Binder.restoreCallingIdentity(oldId);
            return 0;
        }

        public synchronized void endRestoreSession() {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                    "endRestoreSession");