Make IBackupTransport.finishBackup() also return an int code, since it too can
return TRANSPORT_NOT_INITIALIZED (in fact that's typically how it comes).
For consistency, make other IBackupTransport methods return int instead of
boolean, and handle accordingly.
Make initializeDevice() its own method instead of a flag on performBackup().
This will be needed when un-checking the settings box anyway, and is
conceptually unrelated to whatever happens to be the first post-initialization
backup we perform. (Note that even if the init is sent separately from the
backup operation, the server will remember that an init has been done and
will *not* return NOT_INITIALIZED for the subsequent backup.)
Fix LocalTransport accordingly (trivial changes).
Handle failures more robustly in BackupManagerService -- most notably,
doQueuedBackups() was ignoring the result code of processOneBackup(), so
a NOT_INITIALIZED error would go past unseen (at least until the next
backup pass). Keep track of error code returns more universally now.
(This includes finishBackup(), of course.)
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 861f66d..1dc51c8 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -920,52 +920,56 @@
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.
- 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 @@
}
}
- 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 @@
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 @@
}
// 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 @@
// 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 @@
}
}
- 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 @@
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;