Modify the IBackupTransport API to support bulk restore operations.
Change the BackupManagerService and LocalTransport to support the new API.
diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java
index 96a99ae..eeca148 100644
--- a/core/java/android/backup/RestoreSet.java
+++ b/core/java/android/backup/RestoreSet.java
@@ -43,14 +43,14 @@
* transport. This is guaranteed to be valid for the duration of a restore
* session, but is meaningless once the session has ended.
*/
- public int token;
+ public long token;
public RestoreSet() {
// Leave everything zero / null
}
- public RestoreSet(String _name, String _dev, int _token) {
+ public RestoreSet(String _name, String _dev, long _token) {
name = _name;
device = _dev;
token = _token;
@@ -65,7 +65,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeString(device);
- out.writeInt(token);
+ out.writeLong(token);
}
public static final Parcelable.Creator<RestoreSet> CREATOR
@@ -82,6 +82,6 @@
private RestoreSet(Parcel in) {
name = in.readString();
device = in.readString();
- token = in.readInt();
+ token = in.readLong();
}
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 84ed729..ec63528 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -54,63 +54,70 @@
long requestBackupTime();
/**
- * Establish a connection to the back-end data repository, if necessary. If the transport
- * needs to initialize state that is not tied to individual applications' backup operations,
- * this is where it should be done.
- *
- * @return Zero on success; a nonzero error code on failure.
- */
- int startSession();
-
- /**
- * Send one application's data to the backup destination.
+ * 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}
+ * must be called to ensure the data is sent and recorded successfully.
*
* @param packageInfo The identity of the application whose data is being backed up.
* This specifically includes the signature list for the package.
* @param data The data stream that resulted from invoking the application's
* BackupService.doBackup() method. This may be a pipe rather than a file on
* persistent media, so it may not be seekable.
- * @return Zero on success; a nonzero error code on failure.
+ * @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).
*/
- int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor data);
+ boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);
+
+ /**
+ * Finish sending application data to the backup destination. This must be
+ * called after {@link #performBackup} to ensure that all data is sent. Only
+ * when this method returns true can the backup be assumed to have succeeded.
+ *
+ * @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).
+ */
+ boolean finishBackup();
/**
* Get the set of backups currently available over this transport.
*
- * @return Descriptions of the set of restore images available for this device.
+ * @return Descriptions of the set of restore images available for this device,
+ * or null if an error occurred (the attempt should be rescheduled).
**/
RestoreSet[] getAvailableRestoreSets();
/**
- * Get the set of applications from a given restore image.
+ * Start restoring application data from backup. After calling this function,
+ * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
+ * to walk through the actual application data.
*
* @param token A backup token as returned by {@link #getAvailableRestoreSets}.
- * @return An array of PackageInfo objects describing all of the applications
- * available for restore from this restore image. This should include the list
- * of signatures for each package so that the Backup Manager can filter using that
- * information.
+ * @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}).
*/
- PackageInfo[] getAppSet(int token);
+ boolean startRestore(long token, in PackageInfo[] packages);
/**
- * Retrieve one application's data from the backing store.
- *
- * @param token The backup record from which a restore is being requested.
- * @param packageInfo The identity of the application whose data is being restored.
- * This must include the signature list for the package; it is up to the transport
- * to verify that the requested app's signatures match the saved backup record
- * because the transport cannot necessarily trust the client device.
- * @param data An open, writable file into which the backup image should be stored.
- * @return Zero on success; a nonzero error code on failure.
+ * Get the package name of the next application with data in the backup store.
+ * @return The name of one of the packages supplied to {@link #startRestore},
+ * or "" (the empty string) if no more backup data is available,
+ * or null if an error occurred (the restore should be aborted and rescheduled).
*/
- int getRestoreData(int token, in PackageInfo packageInfo, in ParcelFileDescriptor data);
+ String nextRestorePackage();
/**
- * Terminate the backup session, closing files, freeing memory, and cleaning up whatever
- * other state the transport required.
- *
- * @return Zero on success; a nonzero error code on failure. Even on failure, the session
- * is torn down and must be restarted if another backup is attempted.
+ * 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}).
*/
- int endSession();
+ boolean getRestoreData(in ParcelFileDescriptor outFd);
+
+ /**
+ * End a restore session (aborting any in-process data transfer as necessary),
+ * freeing any resources and connections used during the restore process.
+ */
+ void finishRestore();
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 3dd71f3..0fbbb3f 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -33,11 +33,8 @@
private Context mContext;
private PackageManager mPackageManager;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
- private FileFilter mDirFileFilter = new FileFilter() {
- public boolean accept(File f) {
- return f.isDirectory();
- }
- };
+ private PackageInfo[] mRestorePackages = null;
+ private int mRestorePackage = -1; // Index into mRestorePackages
public LocalTransport(Context context) {
@@ -51,21 +48,9 @@
return 0;
}
- public int startSession() throws RemoteException {
- if (DEBUG) Log.v(TAG, "session started");
- mDataDir.mkdirs();
- return 0;
- }
-
- public int endSession() throws RemoteException {
- if (DEBUG) Log.v(TAG, "session ended");
- return 0;
- }
-
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
+ public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
throws RemoteException {
if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
- int err = 0;
File packageDir = new File(mDataDir, packageInfo.packageName);
packageDir.mkdirs();
@@ -101,9 +86,8 @@
try {
entity.write(buf, 0, dataSize);
} catch (IOException e) {
- Log.e(TAG, "Unable to update key file "
- + entityFile.getAbsolutePath());
- err = -1;
+ Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
+ return false;
} finally {
entity.close();
}
@@ -111,14 +95,17 @@
entityFile.delete();
}
}
+ return true;
} catch (IOException e) {
// oops, something went wrong. abort the operation and return error.
- Log.v(TAG, "Exception reading backup input:");
- e.printStackTrace();
- err = -1;
+ Log.v(TAG, "Exception reading backup input:", e);
+ return false;
}
+ }
- return err;
+ public boolean finishBackup() throws RemoteException {
+ if (DEBUG) Log.v(TAG, "finishBackup()");
+ return true;
}
// Restore handling
@@ -129,72 +116,66 @@
return array;
}
- public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
- if (DEBUG) Log.v(TAG, "getting app set " + token);
- // the available packages are the extant subdirs of mDatadir
- File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
- ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
- for (File dir : packageDirs) {
- try {
- PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
- PackageManager.GET_SIGNATURES);
- if (pkg != null) {
- packages.add(pkg);
- }
- } catch (NameNotFoundException e) {
- // restore set contains data for a package not installed on the
- // phone -- just ignore it.
- }
- }
-
- if (DEBUG) {
- Log.v(TAG, "Built app set of " + packages.size() + " entries:");
- for (PackageInfo p : packages) {
- Log.v(TAG, " + " + p.packageName);
- }
- }
-
- PackageInfo[] result = new PackageInfo[packages.size()];
- return packages.toArray(result);
+ public boolean startRestore(long token, PackageInfo[] packages) {
+ if (DEBUG) Log.v(TAG, "start restore " + token);
+ mRestorePackages = packages;
+ mRestorePackage = -1;
+ return true;
}
- public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
- throws android.os.RemoteException {
- if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
- // we only support one hardcoded restore set
- if (token != 0) return -1;
+ public String nextRestorePackage() {
+ if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+ while (++mRestorePackage < mRestorePackages.length) {
+ String name = mRestorePackages[mRestorePackage].packageName;
+ if (new File(mDataDir, name).isDirectory()) {
+ if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
+ return name;
+ }
+ }
- // the data for a given package is at a known location
- File packageDir = new File(mDataDir, packageInfo.packageName);
+ if (DEBUG) Log.v(TAG, " no more packages to restore");
+ return "";
+ }
+
+ public boolean 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);
// 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 (DEBUG) Log.v(TAG, " found " + blobs.length + " key files");
- int err = 0;
- if (blobs != null && blobs.length > 0) {
- BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
- try {
- for (File f : blobs) {
- copyToRestoreData(f, out);
- }
- } catch (Exception e) {
- Log.e(TAG, "Unable to read backup records");
- err = -1;
- }
+ if (blobs == null) {
+ Log.e(TAG, "Error listing directory: " + packageDir);
+ return false; // nextRestorePackage() ensures the dir exists, so this is an error
}
- return err;
+
+ // We expect at least some data if the directory exists in the first place
+ if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files");
+ BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
+ try {
+ for (File f : blobs) {
+ FileInputStream in = new FileInputStream(f);
+ try {
+ int size = (int) f.length();
+ byte[] buf = new byte[size];
+ in.read(buf);
+ String key = new String(Base64.decode(f.getName()));
+ if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size);
+ out.writeEntityHeader(key, size);
+ out.writeEntityData(buf, size);
+ } finally {
+ in.close();
+ }
+ }
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to read backup records", e);
+ return false;
+ }
}
- private void copyToRestoreData(File f, BackupDataOutput out) throws IOException {
- FileInputStream in = new FileInputStream(f);
- int size = (int) f.length();
- byte[] buf = new byte[size];
- in.read(buf);
- String key = new String(Base64.decode(f.getName()));
- if (DEBUG) Log.v(TAG, " ... copy to stream: key=" + key
- + " size=" + size);
- out.writeEntityHeader(key, size);
- out.writeEntityData(buf, size);
+ public void finishRestore() {
+ if (DEBUG) Log.v(TAG, "finishRestore()");
}
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 197404e..fdd4617b 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -351,28 +351,28 @@
void addPackageParticipantsLocked(String packageName) {
// Look for apps that define the android:backupAgent attribute
if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
- List<ApplicationInfo> targetApps = allAgentApps();
+ List<PackageInfo> targetApps = allAgentPackages();
addPackageParticipantsLockedInner(packageName, targetApps);
}
private void addPackageParticipantsLockedInner(String packageName,
- List<ApplicationInfo> targetApps) {
+ List<PackageInfo> targetPkgs) {
if (DEBUG) {
- Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
- for (ApplicationInfo a : targetApps) {
- Log.v(TAG, " " + a + " agent=" + a.backupAgentName);
+ Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
+ for (PackageInfo p : targetPkgs) {
+ Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName);
}
}
- for (ApplicationInfo app : targetApps) {
- if (packageName == null || app.packageName.equals(packageName)) {
- int uid = app.uid;
+ for (PackageInfo pkg : targetPkgs) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
if (set == null) {
set = new HashSet<ApplicationInfo>();
mBackupParticipants.put(uid, set);
}
- set.add(app);
+ set.add(pkg.applicationInfo);
backUpPackageManagerData();
}
}
@@ -382,67 +382,67 @@
// 'packageName' is null, *all* participating apps will be removed.
void removePackageParticipantsLocked(String packageName) {
if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
- List<ApplicationInfo> allApps = null;
+ List<PackageInfo> allApps = null;
if (packageName != null) {
- allApps = new ArrayList<ApplicationInfo>();
+ allApps = new ArrayList<PackageInfo>();
try {
- ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
- allApps.add(app);
+ int flags = PackageManager.GET_SIGNATURES;
+ allApps.add(mPackageManager.getPackageInfo(packageName, flags));
} catch (Exception e) {
- // just skip it
+ // just skip it (???)
}
} else {
// all apps with agents
- allApps = allAgentApps();
+ allApps = allAgentPackages();
}
removePackageParticipantsLockedInner(packageName, allApps);
}
private void removePackageParticipantsLockedInner(String packageName,
- List<ApplicationInfo> agents) {
+ List<PackageInfo> agents) {
if (DEBUG) {
Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
+ ") removing " + agents.size() + " entries");
- for (ApplicationInfo a : agents) {
- Log.v(TAG, " - " + a);
+ for (PackageInfo p : agents) {
+ Log.v(TAG, " - " + p);
}
}
- for (ApplicationInfo app : agents) {
- if (packageName == null || app.packageName.equals(packageName)) {
- int uid = app.uid;
+ for (PackageInfo pkg : agents) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
if (set != null) {
// Find the existing entry with the same package name, and remove it.
// We can't just remove(app) because the instances are different.
for (ApplicationInfo entry: set) {
- if (entry.packageName.equals(app.packageName)) {
+ if (entry.packageName.equals(pkg.packageName)) {
set.remove(entry);
backUpPackageManagerData();
break;
}
}
if (set.size() == 0) {
- mBackupParticipants.delete(uid); }
+ mBackupParticipants.delete(uid);
+ }
}
}
}
}
// Returns the set of all applications that define an android:backupAgent attribute
- private List<ApplicationInfo> allAgentApps() {
+ private List<PackageInfo> allAgentPackages() {
// !!! TODO: cache this and regenerate only when necessary
- List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
- int N = allApps.size();
- if (N > 0) {
- for (int a = N-1; a >= 0; a--) {
- ApplicationInfo app = allApps.get(a);
- if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
- || app.backupAgentName == null) {
- allApps.remove(a);
- }
+ int flags = PackageManager.GET_SIGNATURES;
+ List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+ int N = packages.size();
+ for (int a = N-1; a >= 0; a--) {
+ ApplicationInfo app = packages.get(a).applicationInfo;
+ if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+ || app.backupAgentName == null) {
+ packages.remove(a);
}
}
- return allApps;
+ return packages;
}
// Reset the given package's known backup participants. Unlike add/remove, the update
@@ -455,7 +455,7 @@
if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
// brute force but small code size
- List<ApplicationInfo> allApps = allAgentApps();
+ List<PackageInfo> allApps = allAgentPackages();
removePackageParticipantsLockedInner(packageName, allApps);
addPackageParticipantsLockedInner(packageName, allApps);
}
@@ -578,17 +578,7 @@
public void run() {
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
- // start up the transport
- try {
- mTransport.startSession();
- } catch (Exception e) {
- Log.e(TAG, "Error session transport");
- e.printStackTrace();
- return;
- }
-
- // The transport is up and running. First, back up the package manager
- // metadata if necessary
+ // First, back up the package manager metadata if necessary
boolean doPackageManager;
synchronized (BackupManagerService.this) {
doPackageManager = mDoPackageManager;
@@ -601,7 +591,7 @@
if (DEBUG) Log.i(TAG, "Running PM backup pass as well");
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentApps());
+ mPackageManager, allAgentPackages());
BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
processOneBackup(pmRequest,
@@ -614,10 +604,12 @@
// Finally, tear down the transport
try {
- mTransport.endSession();
- } catch (Exception e) {
- Log.e(TAG, "Error ending transport");
- e.printStackTrace();
+ if (!mTransport.finishBackup()) {
+ // STOPSHIP TODO: handle errors
+ Log.e(TAG, "Backup failure in finishBackup()");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in finishBackup()", e);
}
if (!mJournal.delete()) {
@@ -712,24 +704,18 @@
if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
backupData =
ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
- int error = transport.performBackup(packInfo, backupData);
+ if (!transport.performBackup(packInfo, backupData)) {
+ // STOPSHIP TODO: handle errors
+ Log.e(TAG, "Backup failure in performBackup()");
+ }
// !!! 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();
+ Log.e(TAG, "Error backing up " + packageName, e);
}
}
}
@@ -737,25 +723,6 @@
// ----- Restore handling -----
- // Is the given package restorable on this device? Returns the on-device app's
- // ApplicationInfo struct if it is; null if not.
- //
- // !!! TODO: also consider signatures
- PackageInfo isRestorable(PackageInfo packageInfo) {
- if (packageInfo.packageName != null) {
- try {
- PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
- PackageManager.GET_SIGNATURES);
- if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
- return app;
- }
- } catch (Exception e) {
- // doesn't exist on this device, or other error -- just ignore it.
- }
- }
- return null;
- }
-
private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
@@ -816,130 +783,141 @@
/**
* Restore sequence:
*
- * 1. start up the transport session
- * 2. get the restore set description for our identity
- * 3. for each app in the restore set:
+ * 1. get the restore set description for our identity
+ * 2. 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
- * 4.e. unbind the agent [and kill the app?]
- * 5. shut down the transport
+ * 3. for each app in the restore queue:
+ * 3.a. clear the app data
+ * 3.b. get the restore data for the app from the transport
+ * 3.c. launch the backup agent for the app
+ * 3.d. agent.doRestore() with the data from the server
+ * 3.e. unbind the agent [and kill the app?]
+ * 4. shut down the transport
*/
- int err = -1;
+ // build the set of apps to restore
try {
- err = mTransport.startSession();
- } catch (Exception e) {
- Log.e(TAG, "Error starting transport for restore");
- e.printStackTrace();
- }
+ RestoreSet[] images = mTransport.getAvailableRestoreSets();
+ if (images == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting restore sets");
+ return;
+ }
- if (err == 0) {
- // build the set of apps to restore
- try {
- RestoreSet[] images = mTransport.getAvailableRestoreSets();
- if (images.length > 0) {
- // !!! TODO: pick out the set for this token
- mImage = images[0];
+ if (images.length == 0) {
+ Log.i(TAG, "No restore sets available");
+ return;
+ }
- // Pull the Package Manager metadata from the restore set first
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentApps());
- PackageInfo pmApp = new PackageInfo();
- pmApp.packageName = PACKAGE_MANAGER_SENTINEL;
- // !!! TODO: version currently ignored when 'restoring' the PM metadata
- processOneRestore(pmApp, 0,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+ mImage = images[0];
- // build the set of apps we will attempt to restore
- PackageInfo[] packages = mTransport.getAppSet(mImage.token);
- HashSet<RestoreRequest> appsToRestore = new HashSet<RestoreRequest>();
- for (PackageInfo pkg: packages) {
- // get the real PackageManager idea of the package
- PackageInfo app = isRestorable(pkg);
- if (app != null) {
- // Validate against the backed-up signature block, too
- Metadata info = pmAgent.getRestoredMetadata(app.packageName);
- if (info != null) {
- if (app.versionCode >= info.versionCode) {
- if (DEBUG) Log.v(TAG, "Restore version "
- + info.versionCode
- + " compatible with app version "
- + app.versionCode);
- if (signaturesMatch(info.signatures, app.signatures)) {
- appsToRestore.add(
- new RestoreRequest(app, info.versionCode));
- } else {
- Log.w(TAG, "Sig mismatch restoring "
- + app.packageName);
- }
- } else {
- Log.i(TAG, "Restore set for " + app.packageName
- + " is too new [" + info.versionCode
- + "] for installed app version "
- + app.versionCode);
- }
- } else {
- Log.d(TAG, "Unable to get metadata for "
- + app.packageName);
- }
- }
- }
+ // Get the list of all packages which have backup enabled.
+ // (Include the Package Manager metadata pseudo-package first.)
+ ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
+ PackageInfo omPackage = new PackageInfo();
+ omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+ restorePackages.add(omPackage);
- // now run the restore queue
- doQueuedRestores(appsToRestore);
+ List<PackageInfo> agentPackages = allAgentPackages();
+ restorePackages.addAll(agentPackages);
+
+ // STOPSHIP TODO: pick out the set for this token (instead of images[0])
+ long token = images[0].token;
+ if (!mTransport.startRestore(token, restorePackages.toArray(new PackageInfo[0]))) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error starting restore operation");
+ return;
+ }
+
+ String packageName = mTransport.nextRestorePackage();
+ if (packageName == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting first restore package");
+ return;
+ } else if (packageName.equals("")) {
+ Log.i(TAG, "No restore data available");
+ return;
+ } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+ Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
+ + "\", found only \"" + packageName + "\"");
+ return;
+ }
+
+ // Pull the Package Manager metadata from the restore set first
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, agentPackages);
+ processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+
+ for (;;) {
+ packageName = mTransport.nextRestorePackage();
+ if (packageName == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting next restore package");
+ return;
+ } else if (packageName.equals("")) {
+ break;
}
- } catch (RemoteException e) {
- // can't happen; transports run locally
- }
- // done; shut down the transport
- try {
- mTransport.endSession();
- } catch (Exception e) {
- Log.e(TAG, "Error ending transport for restore");
- e.printStackTrace();
- }
- }
+ Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
+ if (metaInfo == null) {
+ Log.e(TAG, "Missing metadata for " + packageName);
+ continue;
+ }
- // even if the initial session startup failed, report that we're done here
- }
+ int flags = PackageManager.GET_SIGNATURES;
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ if (metaInfo.versionCode > packageInfo.versionCode) {
+ Log.w(TAG, "Package " + packageName
+ + " restore version [" + metaInfo.versionCode
+ + "] is too new for installed version ["
+ + packageInfo.versionCode + "]");
+ continue;
+ }
- // restore each app in the queue
- void doQueuedRestores(HashSet<RestoreRequest> appsToRestore) {
- for (RestoreRequest req : appsToRestore) {
- PackageInfo app = req.app;
- Log.d(TAG, "starting agent for restore of " + app);
+ if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
+ Log.w(TAG, "Signature mismatch restoring " + packageName);
+ continue;
+ }
- try {
- // Remove the app's data first
- clearApplicationDataSynchronous(app.packageName);
+ if (DEBUG) Log.v(TAG, "Package " + packageName
+ + " restore version [" + metaInfo.versionCode
+ + "] is compatible with installed version ["
+ + packageInfo.versionCode + "]");
- // Now perform the restore into the clean app
- IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
+ // Now perform the actual restore
+ clearApplicationDataSynchronous(packageName);
+ IBackupAgent agent = bindToAgentSynchronous(
+ packageInfo.applicationInfo,
IApplicationThread.BACKUP_MODE_RESTORE);
- if (agent != null) {
- processOneRestore(app, req.storedAppVersion, agent);
+ if (agent == null) {
+ Log.w(TAG, "Can't find backup agent for " + packageName);
+ continue;
}
- // unbind even on timeout, just in case
- mActivityManager.unbindBackupAgent(app.applicationInfo);
- } catch (SecurityException ex) {
- // Try for the next one.
- Log.d(TAG, "error in bind", ex);
- } catch (RemoteException e) {
- // can't happen
+ try {
+ processOneRestore(packageInfo, metaInfo.versionCode, agent);
+ } finally {
+ // unbind even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
+ }
}
-
+ } catch (NameNotFoundException e) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Invalid paackage restoring data", e);
+ } catch (RemoteException e) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error restoring data", e);
+ } finally {
+ try {
+ mTransport.finishRestore();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error finishing restore", e);
+ }
}
}
- // Do the guts of a restore of one application, derived from the 'mImage'
- // restore set via the 'mTransport' transport.
- void processOneRestore(PackageInfo app, int storedAppVersion, IBackupAgent agent) {
+ // Do the guts of a restore of one application, using mTransport.getRestoreData().
+ void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
// !!! TODO: actually run the restore through mTransport
final String packageName = app.packageName;
@@ -954,11 +932,12 @@
// 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
+ if (!mTransport.getRestoreData(backupData)) {
+ // STOPSHIP TODO: Handle this error somehow?
+ Log.e(TAG, "Error getting restore data for " + packageName);
+ return;
+ }
} finally {
backupData.close();
}
@@ -973,30 +952,18 @@
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
- boolean success = false;
try {
- agent.doRestore(backupData, storedAppVersion, newState);
- success = true;
- } catch (Exception e) {
- Log.e(TAG, "Restore failed for " + packageName);
- e.printStackTrace();
+ agent.doRestore(backupData, appVersionCode, newState);
} 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);
+ File savedStateName = new File(mStateDir, packageName);
+ newStateName.renameTo(savedStateName);
} catch (Exception e) {
- Log.e(TAG, "Final exception guard in restore:");
- e.printStackTrace();
+ Log.e(TAG, "Error restoring data for " + packageName, e);
}
}
}
@@ -1206,7 +1173,7 @@
mContext.enforceCallingPermission("android.permission.BACKUP",
"endRestoreSession");
- mRestoreTransport.endSession();
+ mRestoreTransport.finishRestore();
mRestoreTransport = null;
synchronized(BackupManagerService.this) {
if (BackupManagerService.this.mActiveRestoreSession == this) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index d620eb1..66fb86d 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -57,7 +57,7 @@
// is stored using the package name as a key)
private static final String GLOBAL_METADATA_KEY = "@meta@";
- private List<ApplicationInfo> mAllApps;
+ private List<PackageInfo> mAllPackages;
private PackageManager mPackageManager;
private HashMap<String, Metadata> mRestoredSignatures;
@@ -73,9 +73,9 @@
// We're constructed with the set of applications that are participating
// in backup. This set changes as apps are installed & removed.
- PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
+ PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
mPackageManager = packageMgr;
- mAllApps = apps;
+ mAllPackages = packages;
mRestoredSignatures = null;
}
@@ -118,8 +118,8 @@
// For each app we have on device, see if we've backed it up yet. If not,
// write its signature block to the output, keyed on the package name.
- for (ApplicationInfo app : mAllApps) {
- String packName = app.packageName;
+ for (PackageInfo pkg : mAllPackages) {
+ String packName = pkg.packageName;
if (!existing.contains(packName)) {
// We haven't stored this app's signatures yet, so we do that now
try {
@@ -186,7 +186,7 @@
}
// Finally, write the new state blob -- just the list of all apps we handled
- writeStateFile(mAllApps, newState);
+ writeStateFile(mAllPackages, newState);
}
// "Restore" here is a misnomer. What we're really doing is reading back the
@@ -327,7 +327,7 @@
}
// Util: write out our new backup state file
- private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
+ private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
@@ -338,8 +338,8 @@
out.write(metaNameBuf);
// now write all the app names too
- for (ApplicationInfo app : apps) {
- byte[] pkgNameBuf = app.packageName.getBytes();
+ for (PackageInfo pkg : pkgs) {
+ byte[] pkgNameBuf = pkg.packageName.getBytes();
out.writeInt(pkgNameBuf.length);
out.write(pkgNameBuf);
}