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

Commit d6bbc544 authored by Christopher Tate's avatar Christopher Tate Committed by Android (Google) Code Review
Browse files

Merge "Harden against transiently unavailable backup transports" into klp-dev

parents 9d5a9434 8f98252a
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
+155 −62
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.