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:
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user