diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java index 96a99aea0deb5..eeca148667f3a 100644 --- a/core/java/android/backup/RestoreSet.java +++ b/core/java/android/backup/RestoreSet.java @@ -43,14 +43,14 @@ public class RestoreSet implements Parcelable { * 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 class RestoreSet implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(name); out.writeString(device); - out.writeInt(token); + out.writeLong(token); } public static final Parcelable.Creator CREATOR @@ -82,6 +82,6 @@ public class RestoreSet implements Parcelable { 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 84ed729cb7ae9..ec6352818ad99 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -54,63 +54,70 @@ interface IBackupTransport { 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 3dd71f3154254..0fbbb3fb06d0d 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -33,11 +33,8 @@ public class LocalTransport extends IBackupTransport.Stub { 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 @@ public class LocalTransport extends IBackupTransport.Stub { 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 @@ public class LocalTransport extends IBackupTransport.Stub { 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 @@ public class LocalTransport extends IBackupTransport.Stub { 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 @@ public class LocalTransport extends IBackupTransport.Stub { 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 packages = new ArrayList(); - 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 + } + + // 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; } - return err; } - 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 197404e895886..fdd4617b58a43 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -351,28 +351,28 @@ class BackupManagerService extends IBackupManager.Stub { void addPackageParticipantsLocked(String packageName) { // Look for apps that define the android:backupAgent attribute if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName); - List targetApps = allAgentApps(); + List targetApps = allAgentPackages(); addPackageParticipantsLockedInner(packageName, targetApps); } private void addPackageParticipantsLockedInner(String packageName, - List targetApps) { + List 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 set = mBackupParticipants.get(uid); if (set == null) { set = new HashSet(); mBackupParticipants.put(uid, set); } - set.add(app); + set.add(pkg.applicationInfo); backUpPackageManagerData(); } } @@ -382,67 +382,67 @@ class BackupManagerService extends IBackupManager.Stub { // 'packageName' is null, *all* participating apps will be removed. void removePackageParticipantsLocked(String packageName) { if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName); - List allApps = null; + List allApps = null; if (packageName != null) { - allApps = new ArrayList(); + allApps = new ArrayList(); 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 agents) { + List 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 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 allAgentApps() { + private List allAgentPackages() { // !!! TODO: cache this and regenerate only when necessary - List 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 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 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName); // brute force but small code size - List allApps = allAgentApps(); + List allApps = allAgentPackages(); removePackageParticipantsLockedInner(packageName, allApps); addPackageParticipantsLockedInner(packageName, allApps); } @@ -578,17 +578,7 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { // 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { // ----- 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 @@ class BackupManagerService extends IBackupManager.Stub { /** * 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 appsToRestore = new HashSet(); - 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 restorePackages = new ArrayList(); + PackageInfo omPackage = new PackageInfo(); + omPackage.packageName = PACKAGE_MANAGER_SENTINEL; + restorePackages.add(omPackage); - // now run the restore queue - doQueuedRestores(appsToRestore); + List 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 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 @@ class BackupManagerService extends IBackupManager.Stub { // 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 d620eb1996651..66fb86d9252d3 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -57,7 +57,7 @@ public class PackageManagerBackupAgent extends BackupAgent { // is stored using the package name as a key) private static final String GLOBAL_METADATA_KEY = "@meta@"; - private List mAllApps; + private List mAllPackages; private PackageManager mPackageManager; private HashMap mRestoredSignatures; @@ -73,9 +73,9 @@ public class PackageManagerBackupAgent extends BackupAgent { // 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 apps) { + PackageManagerBackupAgent(PackageManager packageMgr, List packages) { mPackageManager = packageMgr; - mAllApps = apps; + mAllPackages = packages; mRestoredSignatures = null; } @@ -118,8 +118,8 @@ public class PackageManagerBackupAgent extends BackupAgent { // 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 @@ public class PackageManagerBackupAgent extends BackupAgent { } // 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 @@ public class PackageManagerBackupAgent extends BackupAgent { } // Util: write out our new backup state file - private void writeStateFile(List apps, ParcelFileDescriptor stateFile) { + private void writeStateFile(List pkgs, ParcelFileDescriptor stateFile) { FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); @@ -338,8 +338,8 @@ public class PackageManagerBackupAgent extends BackupAgent { 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); }