diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 47496a91e89d5..a830ebdbce75f 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -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), diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 603f691f95226..4fc3edc76c8d8 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -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; } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 861f66d0144ac..1dc51c8cc72b6 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -920,52 +920,56 @@ 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 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. - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( - mPackageManager, allAgentPackages()); - BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); - pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; + if (status == BackupConstants.TRANSPORT_OK) { + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, allAgentPackages()); + BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); + pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; + status = processOneBackup(pmRequest, + IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); + } - // 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) { + if (status == BackupConstants.TRANSPORT_OK) { + // Now run all the backups in our queue + status = doQueuedBackups(mTransport); + } + + 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, mQueue.size(), millis); + } else { + EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, ""); + Log.e(TAG, "Transport error in finishBackup()"); + } + } + + // 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(); - 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; - } - - // Now run all the backups in our queue - int count = mQueue.size(); - doQueuedBackups(mTransport); - - // Finally, tear down the transport - if (mTransport.finishBackup()) { - int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); - EventLog.writeEvent(BACKUP_SUCCESS_EVENT, count, millis); - } else { - EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, ""); - Log.e(TAG, "Transport error in finishBackup()"); - } - - if (!mJournal.delete()) { - Log.e(TAG, "Unable to remove backup journal file " + mJournal); } } catch (Exception e) { Log.e(TAG, "Error in backup thread", e); @@ -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) { - backupData = ParcelFileDescriptor.open(backupDataName, - ParcelFileDescriptor.MODE_READ_ONLY); + 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. - backupDataName.delete(); - newStateName.renameTo(savedStateName); - EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size); + 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;