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

Commit 28b6d382 authored by Christopher Tate's avatar Christopher Tate Committed by The Android Automerger
Browse files

Harden against transiently unavailable backup transports

The init & clear operations are particularly important to ensure
delivery when at all possible, so we retry those periodically
if the transport is unavailable when we first attempt them.

Now with 100% less build break.

Bug 11716868

Change-Id: I2af4e93788068cfac97c0a48d3568c561eefa23d
parent 0fb8556d
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -187,6 +187,12 @@ public final class Bmgr {
    }

    private void doWipe() {
        String transport = nextArg();
        if (transport == null) {
            showUsage();
            return;
        }

        String pkg = nextArg();
        if (pkg == null) {
            showUsage();
@@ -194,8 +200,8 @@ public final class Bmgr {
        }

        try {
            mBmgr.clearBackupData(pkg);
            System.out.println("Wiped backup data for " + pkg);
            mBmgr.clearBackupData(transport, pkg);
            System.out.println("Wiped backup data for " + pkg + " on " + transport);
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -446,7 +452,7 @@ public final class Bmgr {
        System.err.println("       bmgr restore TOKEN PACKAGE...");
        System.err.println("       bmgr restore PACKAGE");
        System.err.println("       bmgr run");
        System.err.println("       bmgr wipe PACKAGE");
        System.err.println("       bmgr wipe TRANSPORT PACKAGE");
        System.err.println("");
        System.err.println("The 'backup' command schedules a backup pass for the named package.");
        System.err.println("Note that the backup pass will effectively be a no-op if the package");
@@ -462,8 +468,8 @@ public final class Bmgr {
        System.err.println("");
        System.err.println("The 'list transports' command reports the names of the backup transports");
        System.err.println("currently available on the device.  These names can be passed as arguments");
        System.err.println("to the 'transport' command.  The currently selected transport is indicated");
        System.err.println("with a '*' character.");
        System.err.println("to the 'transport' and 'wipe' commands.  The currently selected transport");
        System.err.println("is indicated with a '*' character.");
        System.err.println("");
        System.err.println("The 'list sets' command reports the token and name of each restore set");
        System.err.println("available to the device via the current transport.");
@@ -491,7 +497,8 @@ public final class Bmgr {
        System.err.println("data changes.");
        System.err.println("");
        System.err.println("The 'wipe' command causes all backed-up data for the given package to be");
        System.err.println("erased from the current transport's storage.  The next backup operation");
        System.err.println("erased from the given transport's storage.  The next backup operation");
        System.err.println("that the given application performs will rewrite its entire data set.");
        System.err.println("Transport names to use here are those reported by 'list transports'.");
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -43,14 +43,14 @@ interface IBackupManager {
    void dataChanged(String packageName);

    /**
     * Erase all backed-up data for the given package from the storage
     * Erase all backed-up data for the given package from the given storage
     * destination.
     *
     * Any application can invoke this method for its own package, but
     * only callers who hold the android.permission.BACKUP permission
     * may invoke it for arbitrary packages.
     */
    void clearBackupData(String packageName);
    void clearBackupData(String transportName, String packageName);

    /**
     * Notifies the Backup Manager Service that an agent has become available.  This
+187 −65
Original line number Diff line number Diff line
@@ -161,6 +161,9 @@ class BackupManagerService extends IBackupManager.Stub {
    // the first backup pass.
    private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;

    // Retry interval for clear/init when the transport is unavailable
    private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;

    private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
    private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
    private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
@@ -174,6 +177,8 @@ class BackupManagerService extends IBackupManager.Stub {
    private static final int MSG_RESTORE_TIMEOUT = 8;
    private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
    private static final int MSG_RUN_FULL_RESTORE = 10;
    private static final int MSG_RETRY_INIT = 11;
    private static final int MSG_RETRY_CLEAR = 12;

    // backup task state machine tick
    static final int MSG_BACKUP_RESTORE_STEP = 20;
@@ -306,6 +311,7 @@ class BackupManagerService extends IBackupManager.Stub {

    class RestoreParams {
        public IBackupTransport transport;
        public String dirName;
        public IRestoreObserver observer;
        public long token;
        public PackageInfo pkgInfo;
@@ -313,9 +319,10 @@ class BackupManagerService extends IBackupManager.Stub {
        public boolean needFullBackup;
        public String[] filterSet;

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
                long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
            transport = _transport;
            dirName = _dirName;
            observer = _obs;
            token = _token;
            pkgInfo = _pkg;
@@ -324,9 +331,10 @@ class BackupManagerService extends IBackupManager.Stub {
            filterSet = null;
        }

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
                boolean _needFullBackup) {
        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
                long _token, boolean _needFullBackup) {
            transport = _transport;
            dirName = _dirName;
            observer = _obs;
            token = _token;
            pkgInfo = null;
@@ -335,9 +343,10 @@ class BackupManagerService extends IBackupManager.Stub {
            filterSet = null;
        }

        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
                String[] _filterSet, boolean _needFullBackup) {
        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
                long _token, String[] _filterSet, boolean _needFullBackup) {
            transport = _transport;
            dirName = _dirName;
            observer = _obs;
            token = _token;
            pkgInfo = null;
@@ -357,6 +366,16 @@ class BackupManagerService extends IBackupManager.Stub {
        }
    }

    class ClearRetryParams {
        public String transportName;
        public String packageName;

        ClearRetryParams(String transport, String pkg) {
            transportName = transport;
            packageName = pkg;
        }
    }

    class FullParams {
        public ParcelFileDescriptor fd;
        public final AtomicBoolean latch;
@@ -516,13 +535,28 @@ class BackupManagerService extends IBackupManager.Stub {
                // When it completes successfully, that old journal file will be
                // deleted.  If we crash prior to that, the old journal is parsed
                // at next boot and the journaled requests fulfilled.
                boolean staged = true;
                if (queue.size() > 0) {
                    // Spin up a backup state sequence and set it running
                    PerformBackupTask pbt = new PerformBackupTask(transport, queue, oldJournal);
                    try {
                        String dirName = transport.transportDirName();
                        PerformBackupTask pbt = new PerformBackupTask(transport, dirName,
                                queue, oldJournal);
                        Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
                        sendMessage(pbtMessage);
                    } catch (RemoteException e) {
                        // unable to ask the transport its dir name -- transient failure, since
                        // the above check succeeded.  Try again next time.
                        Slog.e(TAG, "Transport became unavailable attempting backup");
                        staged = false;
                    }
                } else {
                    Slog.v(TAG, "Backup requested but nothing pending");
                    staged = false;
                }

                if (!staged) {
                    // if we didn't actually hand off the wakelock, rewind until next time
                    synchronized (mQueueLock) {
                        mBackupRunning = false;
                    }
@@ -572,7 +606,7 @@ class BackupManagerService extends IBackupManager.Stub {
                RestoreParams params = (RestoreParams)msg.obj;
                Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
                PerformRestoreTask task = new PerformRestoreTask(
                        params.transport, params.observer,
                        params.transport, params.dirName, params.observer,
                        params.token, params.pkgInfo, params.pmToken,
                        params.needFullBackup, params.filterSet);
                Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
@@ -599,6 +633,14 @@ class BackupManagerService extends IBackupManager.Stub {
                break;
            }

            case MSG_RETRY_CLEAR:
            {
                // reenqueues if the transport remains unavailable
                ClearRetryParams params = (ClearRetryParams)msg.obj;
                clearBackupData(params.transportName, params.packageName);
                break;
            }

            case MSG_RUN_INITIALIZE:
            {
                HashSet<String> queue;
@@ -613,6 +655,16 @@ class BackupManagerService extends IBackupManager.Stub {
                break;
            }

            case MSG_RETRY_INIT:
            {
                synchronized (mQueueLock) {
                    recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj);
                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
                            mRunInitIntent);
                }
                break;
            }

            case MSG_RUN_GET_RESTORE_SETS:
            {
                // Like other async operations, this is entered with the wakelock held
@@ -1250,8 +1302,11 @@ class BackupManagerService extends IBackupManager.Stub {
    void recordInitPendingLocked(boolean isPending, String transportName) {
        if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
                + " on transport " + transportName);
        mBackupHandler.removeMessages(MSG_RETRY_INIT);

        try {
            IBackupTransport transport = getTransport(transportName);
            if (transport != null) {
                String transportDirName = transport.transportDirName();
                File stateDir = new File(mBaseStateDir, transportDirName);
                File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
@@ -1271,8 +1326,23 @@ class BackupManagerService extends IBackupManager.Stub {
                    initPendingFile.delete();
                    mPendingInits.remove(transportName);
                }
                return; // done; don't fall through to the error case
            }
        } catch (RemoteException e) {
            // can't happen; the transport is local
            // transport threw when asked its name; fall through to the lookup-failed case
        }

        // The named transport doesn't exist or threw.  This operation is
        // important, so we record the need for a an init and post a message
        // to retry the init later.
        if (isPending) {
            mPendingInits.add(transportName);
            mBackupHandler.sendMessageDelayed(
                    mBackupHandler.obtainMessage(MSG_RETRY_INIT,
                            (isPending ? 1 : 0),
                            0,
                            transportName),
                    TRANSPORT_RETRY_INTERVAL);
        }
    }

@@ -1348,7 +1418,10 @@ class BackupManagerService extends IBackupManager.Stub {
                }
            }
        } catch (RemoteException e) {
            // can't happen, the transport is local
            // the transport threw when asked its file naming prefs; declare it invalid
            Slog.e(TAG, "Unable to register transport as " + name);
            mTransportNames.remove(component);
            mTransports.remove(name);
        }
    }

@@ -1668,7 +1741,7 @@ class BackupManagerService extends IBackupManager.Stub {
                    agent = mConnectedAgent;
                }
            } catch (RemoteException e) {
                // can't happen
                // can't happen - ActivityManager is local
            }
        }
        return agent;
@@ -1844,17 +1917,13 @@ class BackupManagerService extends IBackupManager.Stub {
        int mStatus;
        boolean mFinished;

        public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
                File journal) {
        public PerformBackupTask(IBackupTransport transport, String dirName,
                ArrayList<BackupRequest> queue, File journal) {
            mTransport = transport;
            mOriginalQueue = queue;
            mJournal = journal;

            try {
                mStateDir = new File(mBaseStateDir, transport.transportDirName());
            } catch (RemoteException e) {
                // can't happen; the transport is local
            }
            mStateDir = new File(mBaseStateDir, dirName);

            mCurrentState = BackupState.INITIAL;
            mFinished = false;
@@ -2100,8 +2169,12 @@ class BackupManagerService extends IBackupManager.Stub {
                addBackupTrace("success; recording token");
                try {
                    mCurrentToken = mTransport.getCurrentRestoreSet();
                } catch (RemoteException e) {} // can't happen
                    writeRestoreTokens();
                } catch (RemoteException e) {
                    // nothing for it at this point, unfortunately, but this will be
                    // recorded the next time we fully succeed.
                    addBackupTrace("transport threw returning token");
                }
            }

            // Set up the next backup pass - at this point we can set mBackupRunning
@@ -2322,7 +2395,7 @@ class BackupManagerService extends IBackupManager.Stub {
                addBackupTrace("unbinding " + mCurrentPackage.packageName);
                try {  // unbind even on timeout, just in case
                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
                } catch (RemoteException e) {}
                } catch (RemoteException e) { /* can't happen; activity manager is local */ }
            }
        }

@@ -4337,7 +4410,7 @@ class BackupManagerService extends IBackupManager.Stub {
            }
        }

        PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
        PerformRestoreTask(IBackupTransport transport, String dirName, IRestoreObserver observer,
                long restoreSetToken, PackageInfo targetPackage, int pmToken,
                boolean needFullBackup, String[] filterSet) {
            mCurrentState = RestoreState.INITIAL;
@@ -4360,11 +4433,7 @@ class BackupManagerService extends IBackupManager.Stub {
                mFilterSet = null;
            }

            try {
                mStateDir = new File(mBaseStateDir, transport.transportDirName());
            } catch (RemoteException e) {
                // can't happen; the transport is local
            }
            mStateDir = new File(mBaseStateDir, dirName);
        }

        // Execute one tick of whatever state machine the task implements
@@ -5090,8 +5159,8 @@ class BackupManagerService extends IBackupManager.Stub {
    }

    // Clear the given package's backup data from the current transport
    public void clearBackupData(String packageName) {
        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
    public void clearBackupData(String transportName, String packageName) {
        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
        PackageInfo info;
        try {
            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
@@ -5122,13 +5191,22 @@ class BackupManagerService extends IBackupManager.Stub {

        // Is the given app an available participant?
        if (apps.contains(packageName)) {
            if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
            // found it; fire off the clear request
            if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
            synchronized (mQueueLock) {
                final IBackupTransport transport = getTransport(transportName);
                if (transport == null) {
                    // transport is currently unavailable -- make sure to retry
                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
                            new ClearRetryParams(transportName, packageName));
                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
                    return;
                }
                long oldId = Binder.clearCallingIdentity();
                mWakelock.acquire();
                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
                        new ClearParams(getTransport(mCurrentTransport), info));
                        new ClearParams(transport, info));
                mBackupHandler.sendMessage(msg);
                Binder.restoreCallingIdentity(oldId);
            }
@@ -5626,10 +5704,21 @@ class BackupManagerService extends IBackupManager.Stub {
                + " restoreSet=" + Long.toHexString(restoreSet));

        if (mAutoRestore && mProvisioned && restoreSet != 0) {
            // Do we have a transport to fetch data for us?
            IBackupTransport transport = getTransport(mCurrentTransport);
            if (transport == null) {
                if (DEBUG) Slog.w(TAG, "No transport for install-time restore");
                return;
            }

            try {
                // okay, we're going to attempt a restore of this package from this restore set.
                // The eventual message back into the Package Manager to run the post-install
                // steps for 'token' will be issued from the restore handling code.

                // This can throw and so *must* happen before the wakelock is acquired
                String dirName = transport.transportDirName();

                // We can use a synthetic PackageInfo here because:
                //   1. We know it's valid, since the Package Manager supplied the name
                //   2. Only the packageName field will be used by the restore code
@@ -5638,9 +5727,13 @@ class BackupManagerService extends IBackupManager.Stub {

                mWakelock.acquire();
                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
            msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
                msg.obj = new RestoreParams(transport, dirName, null,
                        restoreSet, pkg, token, true);
                mBackupHandler.sendMessage(msg);
            } catch (RemoteException e) {
                // Binding to the transport broke; back off and proceed with the installation.
                Slog.e(TAG, "Unable to contact transport for install-time restore");
            }
        } else {
            // Auto-restore disabled or no way to attempt a restore; just tell the Package
            // Manager to proceed with the post-install handling for this package.
@@ -5797,13 +5890,23 @@ class BackupManagerService extends IBackupManager.Stub {
                return -1;
            }

            String dirName;
            try {
                dirName = mRestoreTransport.transportDirName();
            } catch (RemoteException e) {
                // Transport went AWOL; fail.
                Slog.e(TAG, "Unable to contact transport for restore");
                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, true);
                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
                                observer, token, true);
                        mBackupHandler.sendMessage(msg);
                        Binder.restoreCallingIdentity(oldId);
                        return 0;
@@ -5857,13 +5960,22 @@ class BackupManagerService extends IBackupManager.Stub {
                return -1;
            }

            String dirName;
            try {
                dirName = mRestoreTransport.transportDirName();
            } catch (RemoteException e) {
                // Transport went AWOL; fail.
                Slog.e(TAG, "Unable to contact transport for restore");
                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,
                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
                                packages, true);
                        mBackupHandler.sendMessage(msg);
                        Binder.restoreCallingIdentity(oldId);
@@ -5929,11 +6041,21 @@ class BackupManagerService extends IBackupManager.Stub {
                return -1;
            }

            String dirName;
            try {
                dirName = mRestoreTransport.transportDirName();
            } catch (RemoteException e) {
                // Transport went AWOL; fail.
                Slog.e(TAG, "Unable to contact transport for restore");
                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, 0, false);
            msg.obj = new RestoreParams(mRestoreTransport, dirName,
                    observer, token, app, 0, false);
            mBackupHandler.sendMessage(msg);
            Binder.restoreCallingIdentity(oldId);
            return 0;