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

Commit 9bbd5ee4 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 26290 into eclair

* changes:
  Make IBackupTransport.finishBackup() also return an int code, since it too can return TRANSPORT_NOT_INITIALIZED (in fact that's typically how it comes).
parents 1167b43c 0144516e
Loading
Loading
Loading
Loading
+26 −38
Original line number Diff line number Diff line
@@ -22,24 +22,6 @@ import android.os.ParcelFileDescriptor;

/** {@hide} */
interface IBackupTransport {
/* STOPSHIP - don't ship with this comment in place
    Things the transport interface has to do:
    1. set up the connection to the destination
        - set up encryption
        - for Google cloud, log in using the user's gaia credential or whatever
        - for adb, just set up the all-in-one destination file
    2. send each app's backup transaction
        - parse the data file for key/value pointers etc
        - send key/blobsize set to the Google cloud, get back quota ok/rejected response
        - sd/adb doesn't preflight; no per-app quota
        - app's entire change is essentially atomic
        - cloud transaction encrypts then sends each key/value pair separately; we already
          parsed the data when preflighting so we don't have to again here
        - sd target streams raw data into encryption envelope then to sd?
    3. shut down connection to destination
        - cloud: tear down connection etc
        - adb: close the file
*/
    /**
     * Ask the transport where, on local device storage, to keep backup state blobs.
     * This is per-transport so that mock transports used for testing can coexist with
@@ -67,6 +49,17 @@ interface IBackupTransport {
     */
    long requestBackupTime();

    /**
     * Initialize the server side storage for this device, erasing all stored data.
     * The transport may send the request immediately, or may buffer it.  After
     * this is called, {@link #finishBackup} must be called to ensure the request
     * is sent and received successfully.
     *
     * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
     *   {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
     */
    int initializeDevice();

    /**
     * Send one application's data to the backup destination.  The transport may send
     * the data immediately, or may buffer it.  After this is called, {@link #finishBackup}
@@ -81,15 +74,12 @@ interface IBackupTransport {
     *   will be erased prior to the storage of the data provided here.  The purpose of this
     *   is to provide a guarantee that no stale data exists in the restore set when the
     *   device begins providing backups.
     * @return If everything is okay so far, returns zero (but {@link #finishBackup} must
     *   still be called).  If the backend dataset has unexpectedly become unavailable,
     *   such as when it is deleted after a period of device inactivity, returns {@link
     *   BackupManager#DATASET_UNAVAILABLE}; in this case, the transport should be
     *   reinitalized and the entire backup pass restarted.  Any other nonzero value is a
     *   fatal error requiring that this package's backup be aborted and rescheduled.
     * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far),
     *  {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
     *  {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
     *  become lost due to inactive expiry or some other reason and needs re-initializing)
     */
    int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd,
            boolean wipeAllFirst);
    int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);

    /**
     * Erase the give application's data from the backup destination.  This clears
@@ -97,10 +87,9 @@ interface IBackupTransport {
     * the app had never yet been backed up.  After this is called, {@link finishBackup}
     * must be called to ensure that the operation is recorded successfully.
     *
     * @return false if errors occurred (the backup should be aborted and rescheduled),
     *   true if everything is OK so far (but {@link #finishBackup} must be called).
     * @return the same error codes as {@link #performBackup}.
     */
    boolean clearBackupData(in PackageInfo packageInfo);
    int clearBackupData(in PackageInfo packageInfo);

    /**
     * Finish sending application data to the backup destination.  This must be
@@ -108,10 +97,9 @@ interface IBackupTransport {
     * all data is sent.  Only when this method returns true can a backup be assumed
     * to have succeeded.
     *
     * @return false if errors occurred (the backup should be aborted and rescheduled),
     *   true if everything is OK.
     * @return the same error codes as {@link #performBackup}.
     */
    boolean finishBackup();
    int finishBackup();

    /**
     * Get the set of backups currently available over this transport.
@@ -129,10 +117,11 @@ interface IBackupTransport {
     * @param token A backup token as returned by {@link #getAvailableRestoreSets}.
     * @param packages List of applications to restore (if data is available).
     *   Application data will be restored in the order given.
     * @return false if errors occurred (the restore should be aborted and rescheduled),
     *   true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
     * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
     *   {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR}
     *   (an error occurred, the restore should be aborted and rescheduled).
     */
    boolean startRestore(long token, in PackageInfo[] packages);
    int startRestore(long token, in PackageInfo[] packages);

    /**
     * Get the package name of the next application with data in the backup store.
@@ -145,10 +134,9 @@ interface IBackupTransport {
    /**
     * Get the data for the application returned by {@link #nextRestorePackage}.
     * @param data An open, writable file into which the backup data should be stored.
     * @return false if errors occurred (the restore should be aborted and rescheduled),
     *   true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
     * @return the same error codes as {@link #nextRestorePackage}.
     */
    boolean getRestoreData(in ParcelFileDescriptor outFd);
    int getRestoreData(in ParcelFileDescriptor outFd);

    /**
     * End a restore session (aborting any in-process data transfer as necessary),
+20 −19
Original line number Diff line number Diff line
@@ -47,25 +47,26 @@ public class LocalTransport extends IBackupTransport.Stub {
    }


    public String transportDirName() throws RemoteException {
    public String transportDirName() {
        return TRANSPORT_DIR_NAME;
    }

    public long requestBackupTime() throws RemoteException {
    public long requestBackupTime() {
        // any time is a good time for local backup
        return 0;
    }

    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data,
            boolean wipeAllFirst) throws RemoteException {
    public int initializeDevice() {
        if (DEBUG) Log.v(TAG, "wiping all data");
        deleteContents(mDataDir);
        return BackupConstants.TRANSPORT_OK;
    }

    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
        if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);

        File packageDir = new File(mDataDir, packageInfo.packageName);
        packageDir.mkdirs();
        if (wipeAllFirst) {
            if (DEBUG) Log.v(TAG, "wiping all data first");
            deleteContents(mDataDir);
        }

        // Each 'record' in the restore set is kept in its own file, named by
        // the record key.  Wind through the data file, extracting individual
@@ -130,7 +131,7 @@ public class LocalTransport extends IBackupTransport.Stub {
        }
    }

    public boolean clearBackupData(PackageInfo packageInfo) {
    public int clearBackupData(PackageInfo packageInfo) {
        if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);

        File packageDir = new File(mDataDir, packageInfo.packageName);
@@ -138,12 +139,12 @@ public class LocalTransport extends IBackupTransport.Stub {
            f.delete();
        }
        packageDir.delete();
        return true;
        return BackupConstants.TRANSPORT_OK;
    }

    public boolean finishBackup() throws RemoteException {
    public int finishBackup() {
        if (DEBUG) Log.v(TAG, "finishBackup()");
        return true;
        return BackupConstants.TRANSPORT_OK;
    }

    // Restore handling
@@ -154,11 +155,11 @@ public class LocalTransport extends IBackupTransport.Stub {
        return array;
    }

    public boolean startRestore(long token, PackageInfo[] packages) {
    public int startRestore(long token, PackageInfo[] packages) {
        if (DEBUG) Log.v(TAG, "start restore " + token);
        mRestorePackages = packages;
        mRestorePackage = -1;
        return true;
        return BackupConstants.TRANSPORT_OK;
    }

    public String nextRestorePackage() {
@@ -175,7 +176,7 @@ public class LocalTransport extends IBackupTransport.Stub {
        return "";
    }

    public boolean getRestoreData(ParcelFileDescriptor outFd) {
    public int getRestoreData(ParcelFileDescriptor outFd) {
        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
        File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
@@ -183,9 +184,9 @@ public class LocalTransport extends IBackupTransport.Stub {
        // The restore set is the concatenation of the individual record blobs,
        // each of which is a file in the package's directory
        File[] blobs = packageDir.listFiles();
        if (blobs == null) {
        if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
            Log.e(TAG, "Error listing directory: " + packageDir);
            return false;  // nextRestorePackage() ensures the dir exists, so this is an error
            return BackupConstants.TRANSPORT_ERROR;
        }

        // We expect at least some data if the directory exists in the first place
@@ -206,10 +207,10 @@ public class LocalTransport extends IBackupTransport.Stub {
                    in.close();
                }
            }
            return true;
            return BackupConstants.TRANSPORT_OK;
        } catch (IOException e) {
            Log.e(TAG, "Unable to read backup records", e);
            return false;
            return BackupConstants.TRANSPORT_ERROR;
        }
    }

+70 −63
Original line number Diff line number Diff line
@@ -920,53 +920,57 @@ class BackupManagerService extends IBackupManager.Stub {

            try {
                EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName());
                int status = BackupConstants.TRANSPORT_OK;

                // If we haven't stored anything yet, we need to do an init operation.
                if (status == BackupConstants.TRANSPORT_OK && mEverStoredApps.size() == 0) {
                    status = mTransport.initializeDevice();
                }

                // The package manager doesn't have a proper <application> etc, but since
                // it's running here in the system process we can just set up its agent
                // directly and use a synthetic BackupRequest.  We always run this pass
                // because it's cheap and this way we guarantee that we don't get out of
                // step even if we're selecting among various transports at run time.
                if (status == BackupConstants.TRANSPORT_OK) {
                    PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
                            mPackageManager, allAgentPackages());
                    BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
                    pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;

                // If we haven't stored anything yet, we need to do an init
                // operation along with recording the metadata blob.
                boolean needInit = (mEverStoredApps.size() == 0);
                int result = processOneBackup(pmRequest,
                        IBackupAgent.Stub.asInterface(pmAgent.onBind()),
                        mTransport, needInit);
                if (result == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
                    // The backend reports that our dataset has been wiped.  We need to
                    // reset all of our bookkeeping and instead run a new backup pass for
                    // everything.
                    EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName());
                    resetBackupState(mStateDir);
                    backupNow();
                    return;
                } else if (result != BackupConstants.TRANSPORT_OK) {
                    // Give up if we couldn't even process the metadata
                    Log.e(TAG, "Meta backup err " + result);
                    return;
                    status = processOneBackup(pmRequest,
                            IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
                }

                if (status == BackupConstants.TRANSPORT_OK) {
                    // Now run all the backups in our queue
                int count = mQueue.size();
                doQueuedBackups(mTransport);
                    status = doQueuedBackups(mTransport);
                }

                // Finally, tear down the transport
                if (mTransport.finishBackup()) {
                if (status == BackupConstants.TRANSPORT_OK) {
                    // Tell the transport to finish everything it has buffered
                    status = mTransport.finishBackup();
                    if (status == BackupConstants.TRANSPORT_OK) {
                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
                    EventLog.writeEvent(BACKUP_SUCCESS_EVENT, count, millis);
                        EventLog.writeEvent(BACKUP_SUCCESS_EVENT, mQueue.size(), millis);
                    } else {
                        EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "");
                        Log.e(TAG, "Transport error in finishBackup()");
                    }
                }

                if (!mJournal.delete()) {
                // When we succeed at everything, we can remove the journal
                if (status == BackupConstants.TRANSPORT_OK && !mJournal.delete()) {
                    Log.e(TAG, "Unable to remove backup journal file " + mJournal);
                }

                if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
                    // The backend reports that our dataset has been wiped.  We need to
                    // reset all of our bookkeeping and instead run a new backup pass for
                    // everything.
                    EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName());
                    resetBackupState(mStateDir);
                    backupNow();
                }
            } catch (Exception e) {
                Log.e(TAG, "Error in backup thread", e);
            } finally {
@@ -975,7 +979,7 @@ class BackupManagerService extends IBackupManager.Stub {
            }
        }

        private void doQueuedBackups(IBackupTransport transport) {
        private int doQueuedBackups(IBackupTransport transport) {
            for (BackupRequest request : mQueue) {
                Log.d(TAG, "starting agent for backup of " + request);

@@ -995,25 +999,26 @@ class BackupManagerService extends IBackupManager.Stub {
                try {
                    agent = bindToAgentSynchronous(request.appInfo, mode);
                    if (agent != null) {
                        processOneBackup(request, agent, transport, false);
                        int result = processOneBackup(request, agent, transport);
                        if (result != BackupConstants.TRANSPORT_OK) return result;
                    }

                    // unbind even on timeout, just in case
                    mActivityManager.unbindBackupAgent(request.appInfo);
                } catch (SecurityException ex) {
                    // Try for the next one.
                    Log.d(TAG, "error in bind/backup", ex);
                } catch (RemoteException e) {
                    Log.v(TAG, "bind/backup threw");
                    e.printStackTrace();
                } finally {
                    try {  // unbind even on timeout, just in case
                        mActivityManager.unbindBackupAgent(request.appInfo);
                    } catch (RemoteException e) {}
                }
            }

            return BackupConstants.TRANSPORT_OK;
        }

        int processOneBackup(BackupRequest request, IBackupAgent agent,
                IBackupTransport transport, boolean doInit) {
        private int processOneBackup(BackupRequest request, IBackupAgent agent,
                IBackupTransport transport) {
            final String packageName = request.appInfo.packageName;
            if (DEBUG) Log.d(TAG, "processOneBackup doBackup(" + doInit + ") on " + packageName);
            if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName);

            File savedStateName = new File(mStateDir, packageName);
            File backupDataName = new File(mDataDir, packageName + ".data");
@@ -1073,26 +1078,23 @@ class BackupManagerService extends IBackupManager.Stub {
            }

            // Now propagate the newly-backed-up data to the transport
            int result = BackupConstants.TRANSPORT_OK;
            try {
                int size = (int) backupDataName.length();
                if (size > 0) {
                    if (result == BackupConstants.TRANSPORT_OK) {
                        backupData = ParcelFileDescriptor.open(backupDataName,
                                ParcelFileDescriptor.MODE_READ_ONLY);
                        result = transport.performBackup(packInfo, backupData);
                    }

                    // TODO - We call finishBackup() for each application backed up, because
                    // we need to know now whether it succeeded or failed.  Instead, we should
                    // hold off on finishBackup() until the end, which implies holding off on
                    // renaming *all* the output state files (see below) until that happens.

                    int performOkay = transport.performBackup(packInfo, backupData, doInit);
                    if (performOkay == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
                        Log.i(TAG, "Backend not initialized");
                        return performOkay;
                    }

                    if ((performOkay != 0) ||
                        !transport.finishBackup()) {
                        throw new Exception("Backup transport failed");
                    if (result == BackupConstants.TRANSPORT_OK) {
                        result = transport.finishBackup();
                    }
                } else {
                    if (DEBUG) Log.i(TAG, "no backup data written; not calling transport");
@@ -1101,18 +1103,22 @@ class BackupManagerService extends IBackupManager.Stub {
                // After successful transport, delete the now-stale data
                // and juggle the files so that next time we supply the agent
                // with the new state file it just created.
                if (result == BackupConstants.TRANSPORT_OK) {
                    backupDataName.delete();
                    newStateName.renameTo(savedStateName);
                    EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size);
                } else {
                    EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
                }
            } catch (Exception e) {
                Log.e(TAG, "Transport error backing up " + packageName, e);
                EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
                return BackupConstants.TRANSPORT_ERROR;
                result = BackupConstants.TRANSPORT_ERROR;
            } finally {
                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
            }

            return BackupConstants.TRANSPORT_OK;
            return result;
        }
    }

@@ -1237,7 +1243,8 @@ class BackupManagerService extends IBackupManager.Stub {
                    }
                }

                if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
                if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
                        BackupConstants.TRANSPORT_OK) {
                    Log.e(TAG, "Error starting restore operation");
                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
                    return;
@@ -1437,7 +1444,7 @@ class BackupManagerService extends IBackupManager.Stub {
                            ParcelFileDescriptor.MODE_CREATE |
                            ParcelFileDescriptor.MODE_TRUNCATE);

                if (!mTransport.getRestoreData(backupData)) {
                if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
                    Log.e(TAG, "Error getting restore data for " + packageName);
                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
                    return;