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).
This commit is contained in:
Android (Google) Code Review
2009-09-21 22:26:35 -04:00
3 changed files with 115 additions and 119 deletions

View File

@@ -22,24 +22,6 @@ import android.os.ParcelFileDescriptor;
/** {@hide} */ /** {@hide} */
interface IBackupTransport { 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. * 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 * This is per-transport so that mock transports used for testing can coexist with
@@ -67,6 +49,17 @@ interface IBackupTransport {
*/ */
long requestBackupTime(); 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 * 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} * 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 * 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 * is to provide a guarantee that no stale data exists in the restore set when the
* device begins providing backups. * device begins providing backups.
* @return If everything is okay so far, returns zero (but {@link #finishBackup} must * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far),
* still be called). If the backend dataset has unexpectedly become unavailable, * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
* such as when it is deleted after a period of device inactivity, returns {@link * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
* BackupManager#DATASET_UNAVAILABLE}; in this case, the transport should be * become lost due to inactive expiry or some other reason and needs re-initializing)
* 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.
*/ */
int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);
boolean wipeAllFirst);
/** /**
* Erase the give application's data from the backup destination. This clears * 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} * 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. * must be called to ensure that the operation is recorded successfully.
* *
* @return false if errors occurred (the backup should be aborted and rescheduled), * @return the same error codes as {@link #performBackup}.
* true if everything is OK so far (but {@link #finishBackup} must be called).
*/ */
boolean clearBackupData(in PackageInfo packageInfo); int clearBackupData(in PackageInfo packageInfo);
/** /**
* Finish sending application data to the backup destination. This must be * 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 * all data is sent. Only when this method returns true can a backup be assumed
* to have succeeded. * to have succeeded.
* *
* @return false if errors occurred (the backup should be aborted and rescheduled), * @return the same error codes as {@link #performBackup}.
* true if everything is OK.
*/ */
boolean finishBackup(); int finishBackup();
/** /**
* Get the set of backups currently available over this transport. * 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 token A backup token as returned by {@link #getAvailableRestoreSets}.
* @param packages List of applications to restore (if data is available). * @param packages List of applications to restore (if data is available).
* Application data will be restored in the order given. * Application data will be restored in the order given.
* @return false if errors occurred (the restore should be aborted and rescheduled), * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
* true if everything is OK so far (go ahead and call {@link #nextRestorePackage}). * {@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. * 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}. * Get the data for the application returned by {@link #nextRestorePackage}.
* @param data An open, writable file into which the backup data should be stored. * @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), * @return the same error codes as {@link #nextRestorePackage}.
* true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
*/ */
boolean getRestoreData(in ParcelFileDescriptor outFd); int getRestoreData(in ParcelFileDescriptor outFd);
/** /**
* End a restore session (aborting any in-process data transfer as necessary), * End a restore session (aborting any in-process data transfer as necessary),

View File

@@ -47,25 +47,26 @@ public class LocalTransport extends IBackupTransport.Stub {
} }
public String transportDirName() throws RemoteException { public String transportDirName() {
return TRANSPORT_DIR_NAME; return TRANSPORT_DIR_NAME;
} }
public long requestBackupTime() throws RemoteException { public long requestBackupTime() {
// any time is a good time for local backup // any time is a good time for local backup
return 0; return 0;
} }
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, public int initializeDevice() {
boolean wipeAllFirst) throws RemoteException { 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); if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
File packageDir = new File(mDataDir, packageInfo.packageName); File packageDir = new File(mDataDir, packageInfo.packageName);
packageDir.mkdirs(); 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 // Each 'record' in the restore set is kept in its own file, named by
// the record key. Wind through the data file, extracting individual // 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); if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
File packageDir = new File(mDataDir, packageInfo.packageName); File packageDir = new File(mDataDir, packageInfo.packageName);
@@ -138,12 +139,12 @@ public class LocalTransport extends IBackupTransport.Stub {
f.delete(); f.delete();
} }
packageDir.delete(); packageDir.delete();
return true; return BackupConstants.TRANSPORT_OK;
} }
public boolean finishBackup() throws RemoteException { public int finishBackup() {
if (DEBUG) Log.v(TAG, "finishBackup()"); if (DEBUG) Log.v(TAG, "finishBackup()");
return true; return BackupConstants.TRANSPORT_OK;
} }
// Restore handling // Restore handling
@@ -154,11 +155,11 @@ public class LocalTransport extends IBackupTransport.Stub {
return array; return array;
} }
public boolean startRestore(long token, PackageInfo[] packages) { public int startRestore(long token, PackageInfo[] packages) {
if (DEBUG) Log.v(TAG, "start restore " + token); if (DEBUG) Log.v(TAG, "start restore " + token);
mRestorePackages = packages; mRestorePackages = packages;
mRestorePackage = -1; mRestorePackage = -1;
return true; return BackupConstants.TRANSPORT_OK;
} }
public String nextRestorePackage() { public String nextRestorePackage() {
@@ -175,7 +176,7 @@ public class LocalTransport extends IBackupTransport.Stub {
return ""; return "";
} }
public boolean getRestoreData(ParcelFileDescriptor outFd) { public int getRestoreData(ParcelFileDescriptor outFd) {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); 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, // The restore set is the concatenation of the individual record blobs,
// each of which is a file in the package's directory // each of which is a file in the package's directory
File[] blobs = packageDir.listFiles(); 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); 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 // 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(); in.close();
} }
} }
return true; return BackupConstants.TRANSPORT_OK;
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Unable to read backup records", e); Log.e(TAG, "Unable to read backup records", e);
return false; return BackupConstants.TRANSPORT_ERROR;
} }
} }

View File

@@ -920,52 +920,56 @@ class BackupManagerService extends IBackupManager.Stub {
try { try {
EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName()); 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 // 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 // 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 // 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 // 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. // step even if we're selecting among various transports at run time.
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( if (status == BackupConstants.TRANSPORT_OK) {
mPackageManager, allAgentPackages()); PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); mPackageManager, allAgentPackages());
pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; 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 if (status == BackupConstants.TRANSPORT_OK) {
// operation along with recording the metadata blob. // Now run all the backups in our queue
boolean needInit = (mEverStoredApps.size() == 0); status = doQueuedBackups(mTransport);
int result = processOneBackup(pmRequest, }
IBackupAgent.Stub.asInterface(pmAgent.onBind()),
mTransport, needInit); if (status == BackupConstants.TRANSPORT_OK) {
if (result == BackupConstants.TRANSPORT_NOT_INITIALIZED) { // 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 // 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 // reset all of our bookkeeping and instead run a new backup pass for
// everything. // everything.
EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName()); EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName());
resetBackupState(mStateDir); resetBackupState(mStateDir);
backupNow(); 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) { } catch (Exception e) {
Log.e(TAG, "Error in backup thread", 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) { for (BackupRequest request : mQueue) {
Log.d(TAG, "starting agent for backup of " + request); Log.d(TAG, "starting agent for backup of " + request);
@@ -995,25 +999,26 @@ class BackupManagerService extends IBackupManager.Stub {
try { try {
agent = bindToAgentSynchronous(request.appInfo, mode); agent = bindToAgentSynchronous(request.appInfo, mode);
if (agent != null) { 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) { } catch (SecurityException ex) {
// Try for the next one. // Try for the next one.
Log.d(TAG, "error in bind/backup", ex); Log.d(TAG, "error in bind/backup", ex);
} catch (RemoteException e) { } finally {
Log.v(TAG, "bind/backup threw"); try { // unbind even on timeout, just in case
e.printStackTrace(); mActivityManager.unbindBackupAgent(request.appInfo);
} catch (RemoteException e) {}
} }
} }
return BackupConstants.TRANSPORT_OK;
} }
int processOneBackup(BackupRequest request, IBackupAgent agent, private int processOneBackup(BackupRequest request, IBackupAgent agent,
IBackupTransport transport, boolean doInit) { IBackupTransport transport) {
final String packageName = request.appInfo.packageName; 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 savedStateName = new File(mStateDir, packageName);
File backupDataName = new File(mDataDir, packageName + ".data"); 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 // Now propagate the newly-backed-up data to the transport
int result = BackupConstants.TRANSPORT_OK;
try { try {
int size = (int) backupDataName.length(); int size = (int) backupDataName.length();
if (size > 0) { if (size > 0) {
backupData = ParcelFileDescriptor.open(backupDataName, if (result == BackupConstants.TRANSPORT_OK) {
ParcelFileDescriptor.MODE_READ_ONLY); backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
result = transport.performBackup(packInfo, backupData);
}
// TODO - We call finishBackup() for each application backed up, because // TODO - We call finishBackup() for each application backed up, because
// we need to know now whether it succeeded or failed. Instead, we should // 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 // hold off on finishBackup() until the end, which implies holding off on
// renaming *all* the output state files (see below) until that happens. // renaming *all* the output state files (see below) until that happens.
int performOkay = transport.performBackup(packInfo, backupData, doInit); if (result == BackupConstants.TRANSPORT_OK) {
if (performOkay == BackupConstants.TRANSPORT_NOT_INITIALIZED) { result = transport.finishBackup();
Log.i(TAG, "Backend not initialized");
return performOkay;
}
if ((performOkay != 0) ||
!transport.finishBackup()) {
throw new Exception("Backup transport failed");
} }
} else { } else {
if (DEBUG) Log.i(TAG, "no backup data written; not calling transport"); 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 // After successful transport, delete the now-stale data
// and juggle the files so that next time we supply the agent // and juggle the files so that next time we supply the agent
// with the new state file it just created. // with the new state file it just created.
backupDataName.delete(); if (result == BackupConstants.TRANSPORT_OK) {
newStateName.renameTo(savedStateName); backupDataName.delete();
EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size); newStateName.renameTo(savedStateName);
EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size);
} else {
EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
}
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Transport error backing up " + packageName, e); Log.e(TAG, "Transport error backing up " + packageName, e);
EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName); EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
return BackupConstants.TRANSPORT_ERROR; result = BackupConstants.TRANSPORT_ERROR;
} finally { } finally {
try { if (backupData != null) backupData.close(); } catch (IOException e) {} 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"); Log.e(TAG, "Error starting restore operation");
EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return; return;
@@ -1437,7 +1444,7 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE); ParcelFileDescriptor.MODE_TRUNCATE);
if (!mTransport.getRestoreData(backupData)) { if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
Log.e(TAG, "Error getting restore data for " + packageName); Log.e(TAG, "Error getting restore data for " + packageName);
EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT); EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return; return;