From 25a747f5c47f25c1a18961b03507f309b84924fe Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Sun, 20 Sep 2009 12:43:58 -0700 Subject: [PATCH] Add an 'init everything' operation to the first backup pass IBackupTransport.performBackup() now takes a flag "wipeAllFirst", which if set will result in the entire restore set for the current device/account being wiped clean prior to the storage of the provided package. This ensures that a device on which backup has just been enabled will not confront potentially-stale information, nor will the restore set potentially contain mismatched data from orphaned packages. The Backup Manager has also been revised to pass this flag when first backing up its master metadata block (and never pass it thereafter unless something has caused the backup state tracking to be erased, e.g. the user has opted out of backup and then later re-enabled it). --- .../internal/backup/IBackupTransport.aidl | 7 +++++- .../internal/backup/LocalTransport.java | 23 +++++++++++++++++-- .../android/server/BackupManagerService.java | 19 ++++++++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index af06965ae1233..250bc9156c5c0 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -77,10 +77,15 @@ interface IBackupTransport { * @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. + * @param wipeAllFirst When true, all backed-up data for the current device/account + * 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 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 performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd); + boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, + boolean wipeAllFirst); /** * Erase the give application's data from the backup destination. This clears diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 2facce2ed1901..981ea82028a6f 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -56,12 +56,16 @@ public class LocalTransport extends IBackupTransport.Stub { return 0; } - public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) - throws RemoteException { + public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, + boolean wipeAllFirst) throws RemoteException { 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 @@ -111,6 +115,21 @@ public class LocalTransport extends IBackupTransport.Stub { } } + // Deletes the contents but not the given directory + private void deleteContents(File dirname) { + File[] contents = dirname.listFiles(); + if (contents != null) { + for (File f : contents) { + if (f.isDirectory()) { + // delete the directory's contents then fall through + // and delete the directory itself. + deleteContents(f); + } + f.delete(); + } + } + } + public boolean clearBackupData(PackageInfo packageInfo) { if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 41863276ed9cc..776de7eabb565 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -949,11 +949,11 @@ class BackupManagerService extends IBackupManager.Stub { } } - void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { + void processOneBackup(BackupRequest request, IBackupAgent agent, + IBackupTransport transport) { final String packageName = request.appInfo.packageName; if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName); - // !!! 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"); @@ -962,6 +962,12 @@ class BackupManagerService extends IBackupManager.Stub { ParcelFileDescriptor backupData = null; ParcelFileDescriptor newState = null; + // Usually we won't force a server-side init, except the first time + // we ever back up following enable of backup. To keep the bookkeeping + // simple, we detect this here rather than maintain state throughout + // the backup manager. + boolean doInit = false; + PackageInfo packInfo; try { // Look up the package info & signatures. This is first so that if it @@ -971,6 +977,13 @@ class BackupManagerService extends IBackupManager.Stub { // The metadata 'package' is synthetic packInfo = new PackageInfo(); packInfo.packageName = packageName; + + // if there's no metadata backup state, this must be the + // first time we've done one since enabling it. + if (savedStateName.exists() == false) { + if (DEBUG) Log.i(TAG, "First backup pass, issuing init"); + doInit = true; + } } else { packInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); @@ -1023,7 +1036,7 @@ class BackupManagerService extends IBackupManager.Stub { // hold off on finishBackup() until the end, which implies holding off on // renaming *all* the output state files (see below) until that happens. - if (!transport.performBackup(packInfo, backupData) || + if (!transport.performBackup(packInfo, backupData, doInit) || !transport.finishBackup()) { throw new Exception("Backup transport failed"); }