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} */
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),

View File

@@ -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;
}
}

View File

@@ -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 <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 @@ 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;