The rest of the basic flow for restore
Also moved the processOneBackup() implementation into the Thread class that runs
the backup sequence.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 42a895c..f871496 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -53,6 +54,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.String;
import java.util.ArrayList;
@@ -70,7 +72,10 @@
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
-
+
+ // Timeout interval for deciding that a bind or clear-data has taken too long
+ static final long TIMEOUT_INTERVAL = 10 * 1000;
+
private Context mContext;
private PackageManager mPackageManager;
private final IActivityManager mActivityManager;
@@ -108,6 +113,10 @@
private IBackupAgent mConnectedAgent;
private volatile boolean mConnecting;
+ // A similar synchronicity mechanism around clearing apps' data for restore
+ private final Object mClearDataLock = new Object();
+ private volatile boolean mClearingData;
+
private int mTransportId;
private File mStateDir;
@@ -207,80 +216,6 @@
}
}
- void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
- final String packageName = request.appInfo.packageName;
- Log.d(TAG, "processOneBackup doBackup() on " + packageName);
-
- try {
- // Look up the package info & signatures. This is first so that if it
- // throws an exception, there's no file setup yet that would need to
- // be unraveled.
- PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNATURES);
-
- // !!! TODO: get the state file dir from the transport
- File savedStateName = new File(mStateDir, packageName);
- File backupDataName = new File(mDataDir, packageName + ".data");
- File newStateName = new File(mStateDir, packageName + ".new");
-
- // In a full backup, we pass a null ParcelFileDescriptor as
- // the saved-state "file"
- ParcelFileDescriptor savedState = (request.fullBackup) ? null
- : ParcelFileDescriptor.open(savedStateName,
- ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE);
-
- backupDataName.delete();
- ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
-
- newStateName.delete();
- ParcelFileDescriptor newState =
- ParcelFileDescriptor.open(newStateName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
-
- // Run the target's backup pass
- boolean success = false;
- try {
- agent.doBackup(savedState, backupData, newState);
- success = true;
- } finally {
- if (savedState != null) {
- savedState.close();
- }
- backupData.close();
- newState.close();
- }
-
- // Now propagate the newly-backed-up data to the transport
- if (success) {
- if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
- backupData =
- ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
- int error = transport.performBackup(packInfo, backupData);
-
- // !!! TODO: After successful transport, delete the now-stale data
- // and juggle the files so that next time the new state is passed
- //backupDataName.delete();
- newStateName.renameTo(savedStateName);
- }
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Package not found on backup: " + packageName);
- } catch (FileNotFoundException fnf) {
- Log.w(TAG, "File not found on backup: ");
- fnf.printStackTrace();
- } catch (RemoteException e) {
- Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
- e.printStackTrace();
- } catch (Exception e) {
- Log.w(TAG, "Final exception guard in backup: ");
- e.printStackTrace();
- }
- }
-
// Add the backup agents in the given package to our set of known backup participants.
// If 'packageName' is null, adds all backup agents in the whole system.
void addPackageParticipantsLocked(String packageName) {
@@ -424,11 +359,14 @@
Log.d(TAG, "awaiting agent for " + app);
// success; wait for the agent to arrive
- while (mConnecting && mConnectedAgent == null) {
+ // only wait 10 seconds for the clear data to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mConnecting && mConnectedAgent == null
+ && (System.currentTimeMillis() < timeoutMark)) {
try {
- mAgentConnectLock.wait(10000);
+ mAgentConnectLock.wait(5000);
} catch (InterruptedException e) {
- // just retry
+ // just bail
return null;
}
}
@@ -447,6 +385,37 @@
return agent;
}
+ // clear an application's data, blocking until the operation completes or times out
+ void clearApplicationDataSynchronous(String packageName) {
+ ClearDataObserver observer = new ClearDataObserver();
+
+ synchronized(mClearDataLock) {
+ mClearingData = true;
+ mPackageManager.clearApplicationUserData(packageName, observer);
+
+ // only wait 10 seconds for the clear data to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+ try {
+ mClearDataLock.wait(5000);
+ } catch (InterruptedException e) {
+ // won't happen, but still.
+ mClearingData = false;
+ }
+ }
+ }
+ }
+
+ class ClearDataObserver extends IPackageDataObserver.Stub {
+ public void onRemoveCompleted(String packageName, boolean succeeded)
+ throws android.os.RemoteException {
+ synchronized(mClearDataLock) {
+ mClearingData = false;
+ notifyAll();
+ }
+ }
+ }
+
// ----- Back up a set of applications via a worker thread -----
class PerformBackupThread extends Thread {
@@ -508,13 +477,88 @@
mActivityManager.unbindBackupAgent(request.appInfo);
} catch (SecurityException ex) {
// Try for the next one.
- Log.d(TAG, "error in bind", ex);
+ Log.d(TAG, "error in bind/backup", ex);
} catch (RemoteException e) {
- // can't happen
+ Log.v(TAG, "bind/backup threw");
+ e.printStackTrace();
}
}
}
+
+ void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
+ final String packageName = request.appInfo.packageName;
+ Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+
+ try {
+ // Look up the package info & signatures. This is first so that if it
+ // throws an exception, there's no file setup yet that would need to
+ // be unraveled.
+ PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNATURES);
+
+ // !!! TODO: get the state file dir from the transport
+ File savedStateName = new File(mStateDir, packageName);
+ File backupDataName = new File(mDataDir, packageName + ".data");
+ File newStateName = new File(mStateDir, packageName + ".new");
+
+ // In a full backup, we pass a null ParcelFileDescriptor as
+ // the saved-state "file"
+ ParcelFileDescriptor savedState = (request.fullBackup) ? null
+ : ParcelFileDescriptor.open(savedStateName,
+ ParcelFileDescriptor.MODE_READ_ONLY |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ backupDataName.delete();
+ ParcelFileDescriptor backupData =
+ ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ newStateName.delete();
+ ParcelFileDescriptor newState =
+ ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ // Run the target's backup pass
+ boolean success = false;
+ try {
+ agent.doBackup(savedState, backupData, newState);
+ success = true;
+ } finally {
+ if (savedState != null) {
+ savedState.close();
+ }
+ backupData.close();
+ newState.close();
+ }
+
+ // Now propagate the newly-backed-up data to the transport
+ if (success) {
+ if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
+ backupData =
+ ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
+ int error = transport.performBackup(packInfo, backupData);
+
+ // !!! TODO: After successful transport, delete the now-stale data
+ // and juggle the files so that next time the new state is passed
+ //backupDataName.delete();
+ newStateName.renameTo(savedStateName);
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Package not found on backup: " + packageName);
+ } catch (FileNotFoundException fnf) {
+ Log.w(TAG, "File not found on backup: ");
+ fnf.printStackTrace();
+ } catch (RemoteException e) {
+ Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
+ e.printStackTrace();
+ } catch (Exception e) {
+ Log.w(TAG, "Final exception guard in backup: ");
+ e.printStackTrace();
+ }
+ }
}
@@ -524,12 +568,12 @@
// ApplicationInfo struct if it is; null if not.
//
// !!! TODO: also consider signatures
- ApplicationInfo isRestorable(PackageInfo packageInfo) {
+ PackageInfo isRestorable(PackageInfo packageInfo) {
if (packageInfo.packageName != null) {
try {
- ApplicationInfo app = mPackageManager.getApplicationInfo(packageInfo.packageName,
+ PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
PackageManager.GET_SIGNATURES);
- if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
+ if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
return app;
}
} catch (Exception e) {
@@ -541,6 +585,7 @@
class PerformRestoreThread extends Thread {
private IBackupTransport mTransport;
+ private RestoreSet mImage;
PerformRestoreThread(IBackupTransport transport) {
mTransport = transport;
@@ -556,6 +601,7 @@
* 3. for each app in the restore set:
* 3.a. if it's restorable on this device, add it to the restore queue
* 4. for each app in the restore queue:
+ * 4.a. clear the app data
* 4.b. get the restore data for the app from the transport
* 4.c. launch the backup agent for the app
* 4.d. agent.doRestore() with the data from the server
@@ -577,13 +623,14 @@
RestoreSet[] images = mTransport.getAvailableRestoreSets();
if (images.length > 0) {
// !!! for now we always take the first set
- RestoreSet image = images[0];
+ mImage = images[0];
// build the set of apps we will attempt to restore
- PackageInfo[] packages = mTransport.getAppSet(image.token);
- HashSet<ApplicationInfo> appsToRestore = new HashSet<ApplicationInfo>();
+ PackageInfo[] packages = mTransport.getAppSet(mImage.token);
+ HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>();
for (PackageInfo pkg: packages) {
- ApplicationInfo app = isRestorable(pkg);
+ // get the real PackageManager idea of the package
+ PackageInfo app = isRestorable(pkg);
if (app != null) {
appsToRestore.add(app);
}
@@ -609,19 +656,23 @@
}
// restore each app in the queue
- void doQueuedRestores(HashSet<ApplicationInfo> appsToRestore) {
- for (ApplicationInfo app : appsToRestore) {
+ void doQueuedRestores(HashSet<PackageInfo> appsToRestore) {
+ for (PackageInfo app : appsToRestore) {
Log.d(TAG, "starting agent for restore of " + app);
- IBackupAgent agent = null;
try {
- agent = bindToAgentSynchronous(app, IApplicationThread.BACKUP_MODE_RESTORE);
+ // Remove the app's data first
+ clearApplicationDataSynchronous(app.packageName);
+
+ // Now perform the restore into the clean app
+ IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
+ IApplicationThread.BACKUP_MODE_RESTORE);
if (agent != null) {
processOneRestore(app, agent);
}
// unbind even on timeout, just in case
- mActivityManager.unbindBackupAgent(app);
+ mActivityManager.unbindBackupAgent(app.applicationInfo);
} catch (SecurityException ex) {
// Try for the next one.
Log.d(TAG, "error in bind", ex);
@@ -632,9 +683,67 @@
}
}
- // do the guts of a restore
- void processOneRestore(ApplicationInfo app, IBackupAgent agent) {
+ // Do the guts of a restore of one application, derived from the 'mImage'
+ // restore set via the 'mTransport' transport.
+ void processOneRestore(PackageInfo app, IBackupAgent agent) {
// !!! TODO: actually run the restore through mTransport
+ final String packageName = app.packageName;
+
+ // !!! TODO: get the dirs from the transport
+ File backupDataName = new File(mDataDir, packageName + ".restore");
+ backupDataName.delete();
+ try {
+ ParcelFileDescriptor backupData =
+ ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ // Run the transport's restore pass
+ // Run the target's backup pass
+ int err = -1;
+ try {
+ err = mTransport.getRestoreData(mImage.token, app, backupData);
+ } catch (RemoteException e) {
+ // can't happen
+ } finally {
+ backupData.close();
+ }
+
+ // Okay, we have the data. Now have the agent do the restore.
+ File newStateName = new File(mStateDir, packageName + ".new");
+ ParcelFileDescriptor newState =
+ ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ backupData = ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+
+ boolean success = false;
+ try {
+ agent.doRestore(backupData, newState);
+ success = true;
+ } catch (Exception e) {
+ Log.e(TAG, "Restore failed for " + packageName);
+ e.printStackTrace();
+ } finally {
+ newState.close();
+ backupData.close();
+ }
+
+ // if everything went okay, remember the recorded state now
+ if (success) {
+ File savedStateName = new File(mStateDir, packageName);
+ newStateName.renameTo(savedStateName);
+ }
+ } catch (FileNotFoundException fnfe) {
+ Log.v(TAG, "Couldn't open file for restore: " + fnfe);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Unable to process restore file: " + ioe);
+ } catch (Exception e) {
+ Log.e(TAG, "Final exception guard in restore:");
+ e.printStackTrace();
+ }
}
}