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"); }