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

Commit 5cb5e89d authored by Christopher Tate's avatar Christopher Tate
Browse files

Fix adb backup/restore

* Exclude key/value-only backup participants until we have a chance to
  augment the archive format with proper handling.

* Don't back up 'stopped' apps, which would un-stop them

* Fix unspecified-user bindService/startActivity invocations

* Teach adb restore about the onRestoreFinished() lifecycle method

* Implement proper app timeout handling in the adb data flows

* Backstop wallpaper backup against rare leftover-state issues

Bug 28056941

Change-Id: Ia59c71a2c74a632a2c2a527b9b7374229c440d46
parent 0bf31c3f
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -92,6 +92,11 @@ public class WallpaperBackupAgent extends BackupAgent {
                if (DEBUG) {
                    Slog.v(TAG, "Wallpaper is backup-eligible; linking & writing");
                }

                // In case of prior muddled state
                infoStage.delete();
                imageStage.delete();

                Os.link(mWallpaperInfo.getCanonicalPath(), infoStage.getCanonicalPath());
                fullBackupFile(infoStage, data);
                Os.link(mWallpaperFile.getCanonicalPath(), imageStage.getCanonicalPath());
+136 −12
Original line number Diff line number Diff line
@@ -725,6 +725,19 @@ public class BackupManagerService {
        return true;
    }

    /* adb backup: is this app only capable of doing key/value?  We say otherwise if
     * the app has a backup agent and does not say fullBackupOnly, *unless* it
     * is a package that we know _a priori_ explicitly supports both key/value and
     * full-data backup.
     */
    private static boolean appIsKeyValueOnly(PackageInfo pkg) {
        if ("com.android.providers.settings".equals(pkg.packageName)) {
            return false;
        }

        return !appGetsFullBackup(pkg);
    }

    // ----- Asynchronous backup/restore handler thread -----

    private class BackupHandler extends Handler {
@@ -3446,8 +3459,8 @@ public class BackupManagerService {
            Intent obbIntent = new Intent().setComponent(new ComponentName(
                    "com.android.sharedstoragebackup",
                    "com.android.sharedstoragebackup.ObbBackupService"));
            BackupManagerService.this.mContext.bindService(
                    obbIntent, this, Context.BIND_AUTO_CREATE);
            BackupManagerService.this.mContext.bindServiceAsUser(
                    obbIntent, this, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
        }

        public void tearDown() {
@@ -3965,7 +3978,7 @@ public class BackupManagerService {
    }

    // Full backup task variant used for adb backup
    class PerformAdbBackupTask extends FullBackupTask {
    class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
        FullBackupEngine mBackupEngine;
        final AtomicBoolean mLatch;

@@ -3979,6 +3992,7 @@ public class BackupManagerService {
        boolean mIncludeSystem;
        boolean mCompress;
        ArrayList<String> mPackages;
        PackageInfo mCurrentTarget;
        String mCurrentPassword;
        String mEncryptPassword;

@@ -4009,6 +4023,9 @@ public class BackupManagerService {
            } else {
                mEncryptPassword = encryptPassword;
            }
            if (MORE_DEBUG) {
                Slog.w(TAG, "Encrypting backup with passphrase=" + mEncryptPassword);
            }
            mCompress = doCompress;
        }

@@ -4165,7 +4182,9 @@ public class BackupManagerService {
            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
            while (iter.hasNext()) {
                PackageInfo pkg = iter.next().getValue();
                if (!appIsEligibleForBackup(pkg.applicationInfo)) {
                if (!appIsEligibleForBackup(pkg.applicationInfo)
                        || appIsStopped(pkg.applicationInfo)
                        || appIsKeyValueOnly(pkg)) {
                    iter.remove();
                }
            }
@@ -4267,9 +4286,11 @@ public class BackupManagerService {
                    final boolean isSharedStorage =
                            pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);

                    mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, null);
                    mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, this);
                    sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);

                    // Don't need to check preflight result as there is no preflight hook.
                    mCurrentTarget = pkg;
                    mBackupEngine.backupOnePackage();

                    // after the app's agent runs to handle its private filesystem
@@ -4308,6 +4329,28 @@ public class BackupManagerService {
                mWakelock.release();
            }
        }

        // BackupRestoreTask methods, used for timeout handling
        @Override
        public void execute() {
            // Unused
        }

        @Override
        public void operationComplete(long result) {
            // Unused
        }

        @Override
        public void handleTimeout() {
            final PackageInfo target = mCurrentTarget;
            if (DEBUG) {
                Slog.w(TAG, "adb backup timeout of " + target);
            }
            if (target != null) {
                tearDownAgentAndKill(mCurrentTarget.applicationInfo);
            }
        }
    }

    // Full backup task extension used for transport-oriented operation
@@ -5255,7 +5298,7 @@ public class BackupManagerService {
        byte[] mWidgetData = null;

        // Runner that can be placed in a separate thread to do in-process
        // invocations of the full restore API asynchronously
        // invocations of the full restore API asynchronously. Used by adb restore.
        class RestoreFileRunnable implements Runnable {
            IBackupAgent mAgent;
            FileMetadata mInfo;
@@ -6404,6 +6447,46 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF

    // ***** end new engine class ***

    // Used for synchronizing doRestoreFinished during adb restore
    class AdbRestoreFinishedLatch implements BackupRestoreTask {
        static final String TAG = "AdbRestoreFinishedLatch";
        final CountDownLatch mLatch;

        AdbRestoreFinishedLatch() {
            mLatch = new CountDownLatch(1);
        }

        void await() {
            boolean latched = false;
            try {
                latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                Slog.w(TAG, "Interrupted!");
            }
        }

        @Override
        public void execute() {
            // Unused
        }

        @Override
        public void operationComplete(long result) {
            if (MORE_DEBUG) {
                Slog.w(TAG, "adb onRestoreFinished() complete");
            }
            mLatch.countDown();
        }

        @Override
        public void handleTimeout() {
            if (DEBUG) {
                Slog.w(TAG, "adb onRestoreFinished() timed out");
            }
            mLatch.countDown();
        }
    }

    class PerformAdbRestoreTask implements Runnable {
        ParcelFileDescriptor mInputFile;
        String mCurrentPassword;
@@ -6419,6 +6502,27 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF

        long mBytes;

        // Runner that can be placed on a separate thread to do in-process invocation
        // of the "restore finished" API asynchronously.  Used by adb restore.
        class RestoreFinishedRunnable implements Runnable {
            final IBackupAgent mAgent;
            final int mToken;

            RestoreFinishedRunnable(IBackupAgent agent, int token) {
                mAgent = agent;
                mToken = token;
            }

            @Override
            public void run() {
                try {
                    mAgent.doRestoreFinished(mToken, mBackupManagerBinder);
                } catch (RemoteException e) {
                    // never happens; this is used only for local binder calls
                }
            }
        }

        // possible handling states for a given package in the restore dataset
        final HashMap<String, RestorePolicy> mPackagePolicies
                = new HashMap<String, RestorePolicy>();
@@ -6560,7 +6664,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                Slog.e(TAG, "Unable to read restore input");
            } finally {
                tearDownPipes();
                tearDownAgent(mTargetApp);
                tearDownAgent(mTargetApp, true);

                try {
                    if (rawDataIn != null) rawDataIn.close();
@@ -6714,7 +6818,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
                            // Now we're really done
                            tearDownPipes();
                            tearDownAgent(mTargetApp);
                            tearDownAgent(mTargetApp, true);
                            mTargetApp = null;
                            mAgentPackage = null;
                        }
@@ -6936,10 +7040,12 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                            // okay, if the remote end failed at any point, deal with
                            // it by ignoring the rest of the restore on it
                            if (!agentSuccess) {
                                if (DEBUG) {
                                    Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring");
                                }
                                mBackupHandler.removeMessages(MSG_TIMEOUT);
                                tearDownPipes();
                                tearDownAgent(mTargetApp);
                                mAgent = null;
                                tearDownAgent(mTargetApp, false);
                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
                            }
                        }
@@ -6988,9 +7094,27 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
            }
        }

        void tearDownAgent(ApplicationInfo app) {
        void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
            if (mAgent != null) {
                try {
                    // In the adb restore case, we do restore-finished here
                    if (doRestoreFinished) {
                        final int token = generateToken();
                        final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch();
                        prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, latch);
                        if (mTargetApp.processName.equals("system")) {
                            if (MORE_DEBUG) {
                                Slog.d(TAG, "system agent - restoreFinished on thread");
                            }
                            Runnable runner = new RestoreFinishedRunnable(mAgent, token);
                            new Thread(runner, "restore-sys-finished-runner").start();
                        } else {
                            mAgent.doRestoreFinished(token, mBackupManagerBinder);
                        }

                        latch.await();
                    }

                    // unbind and tidy up even on timeout or failure, just in case
                    mActivityManager.unbindBackupAgent(app);

@@ -9354,7 +9478,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                    "com.android.backupconfirm.BackupRestoreConfirmation");
            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mContext.startActivity(confIntent);
            mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
        } catch (ActivityNotFoundException e) {
            return false;
        }