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

Commit 44a27903 authored by Christopher Tate's avatar Christopher Tate
Browse files

Make backup/restore asynchronous and enforce timeouts

Callouts to app backup agents are now asynchronous, and timeouts are applied if
they take too long, hang, etc.  The initial timeouts are set to 15 seconds on
backup, 60 seconds on restore.  These operations typically run at background
priority, so it's necessary to give them ample time to run.

As part of setting up this asynchronicity, the Backup Manager's internal thread
management has been overhauled.  It now spins off a single HandlerThread at
startup, and runs backup/restore/etc operations *synchronously* in that thread,
applying timeouts as appropriate.  This means we're no longer spinning up new
threads all the time, and furthermore it ensures that we can never have more
than one operation in flight at once.  Later CLs will remove the now-redundant
logic that previously ensured that operations didn't stomp on each other.

Bug: 2053560
Change-Id: Ie4315c219c7ff6dd8f51f2ad6c0872595b18cff1
parent 4d3cef34
Loading
Loading
Loading
Loading
+22 −12
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.app;
import android.app.IBackupAgent;
import android.backup.BackupDataInput;
import android.backup.BackupDataOutput;
import android.backup.IBackupManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Binder;
@@ -95,11 +96,8 @@ public abstract class BackupAgent extends ContextWrapper {

    // ----- Core implementation -----

    /**
     * Returns the private interface called by the backup system.  Applications will
     * not typically override this.
     */
    public IBinder onBind() {
    /** @hide */
    public final IBinder onBind() {
        return mBinder;
    }

@@ -116,9 +114,10 @@ public abstract class BackupAgent extends ContextWrapper {

        public void doBackup(ParcelFileDescriptor oldState,
                ParcelFileDescriptor data,
                ParcelFileDescriptor newState) throws RemoteException {
                ParcelFileDescriptor newState,
                int token, IBackupManager callbackBinder) throws RemoteException {
            // Ensure that we're running with the app's normal permission level
            long token = Binder.clearCallingIdentity();
            long ident = Binder.clearCallingIdentity();

            if (DEBUG) Log.v(TAG, "doBackup() invoked");
            BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
@@ -131,14 +130,20 @@ public abstract class BackupAgent extends ContextWrapper {
                Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                throw ex;
            } finally {
                Binder.restoreCallingIdentity(token);
                Binder.restoreCallingIdentity(ident);
                try {
                    callbackBinder.opComplete(token);
                } catch (RemoteException e) {
                    // we'll time out anyway, so we're safe
                }
            }
        }

        public void doRestore(ParcelFileDescriptor data, int appVersionCode,
                ParcelFileDescriptor newState) throws RemoteException {
                ParcelFileDescriptor newState,
                int token, IBackupManager callbackBinder) throws RemoteException {
            // Ensure that we're running with the app's normal permission level
            long token = Binder.clearCallingIdentity();
            long ident = Binder.clearCallingIdentity();

            if (DEBUG) Log.v(TAG, "doRestore() invoked");
            BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
@@ -151,7 +156,12 @@ public abstract class BackupAgent extends ContextWrapper {
                Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                throw ex;
            } finally {
                Binder.restoreCallingIdentity(token);
                Binder.restoreCallingIdentity(ident);
                try {
                    callbackBinder.opComplete(token);
                } catch (RemoteException e) {
                    // we'll time out anyway, so we're safe
                }
            }
        }
    }
+20 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app;

import android.backup.IBackupManager;
import android.os.ParcelFileDescriptor;
 
/**
@@ -25,7 +26,7 @@ import android.os.ParcelFileDescriptor;
 *
 * {@hide}
 */ 
interface IBackupAgent {
oneway interface IBackupAgent {
    /**
     * Request that the app perform an incremental backup.
     *
@@ -39,10 +40,18 @@ interface IBackupAgent {
     *
     * @param newState Read-write file, empty when onBackup() is called,
     *        where the new state blob is to be recorded.
     *
     * @param token Opaque token identifying this transaction.  This must
     *        be echoed back to the backup service binder once the new
     *        data has been written to the data and newState files.
     *
     * @param callbackBinder Binder on which to indicate operation completion,
     *        passed here as a convenience to the agent.
     */
    void doBackup(in ParcelFileDescriptor oldState,
            in ParcelFileDescriptor data,
            in ParcelFileDescriptor newState);
            in ParcelFileDescriptor newState,
            int token, IBackupManager callbackBinder);

    /**
     * Restore an entire data snapshot to the application.
@@ -58,7 +67,15 @@ interface IBackupAgent {
     * @param newState Read-write file, empty when onRestore() is called,
     *        that is to be written with the state description that holds after
     *        the restore has been completed.
     *
     * @param token Opaque token identifying this transaction.  This must
     *        be echoed back to the backup service binder once the agent is
     *        finished restoring the application based on the restore data
     *        contents.
     *
     * @param callbackBinder Binder on which to indicate operation completion,
     *        passed here as a convenience to the agent.
     */
    void doRestore(in ParcelFileDescriptor data, int appVersionCode,
            in ParcelFileDescriptor newState);
            in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder);
}
+10 −0
Original line number Diff line number Diff line
@@ -130,4 +130,14 @@ interface IBackupManager {
     * @return An interface to the restore session, or null on error.
     */
    IRestoreSession beginRestoreSession(String transportID);

    /**
     * Notify the backup manager that a BackupAgent has completed the operation
     * corresponding to the given token.
     *
     * @param token The transaction token passed to a BackupAgent's doBackup() or
     *        doRestore() method.
     * {@hide}
     */
    void opComplete(int token);
}
+1 −3
Original line number Diff line number Diff line
@@ -109,9 +109,7 @@ public class RestoreSession {

    /*
     * We wrap incoming binder calls with a private class implementation that
     * redirects them into main-thread actions.  This accomplishes two things:
     * first, it ensures that the app's code is run on their own main thread,
     * never with system Binder identity; and second, it serializes the restore
     * redirects them into main-thread actions.  This serializes the restore
     * progress callbacks nicely within the usual main-thread lifecycle pattern.
     */
    private class RestoreObserverWrapper extends IRestoreObserver.Stub {
+207 −106

File changed.

Preview size limit exceeded, changes collapsed.