diff options
4 files changed, 135 insertions, 3 deletions
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl index 1f11762..d4933ac 100644 --- a/core/java/android/backup/IBackupManager.aidl +++ b/core/java/android/backup/IBackupManager.aidl @@ -32,10 +32,24 @@ interface IBackupManager { /** * Tell the system service that the caller has made changes to its * data, and therefore needs to undergo an incremental backup pass. + * + * Any application can invoke this method for its own package, but + * only callers who hold the android.permission.BACKUP permission + * may invoke it for arbitrary packages. */ void dataChanged(String packageName); /** + * Erase all backed-up data for the given package from the storage + * destination. + * + * Any application can invoke this method for its own package, but + * only callers who hold the android.permission.BACKUP permission + * may invoke it for arbitrary packages. + */ + void clearBackupData(String packageName); + + /** * Notifies the Backup Manager Service that an agent has become available. This * method is only invoked by the Activity Manager. */ diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 4bef265..af06965 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -83,13 +83,25 @@ interface IBackupTransport { 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. + * Erase the give application's data from the backup destination. This clears + * out the given package's data from the current backup set, making it as though + * the app had never yet been backed up. After this is called, {@link finishBackup} + * must be called to ensure that the operation is recorded successfully. * * @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 clearBackupData(in PackageInfo packageInfo); + + /** + * Finish sending application data to the backup destination. This must be + * called after {@link #performBackup} or {@link clearBackupData} to ensure that + * all data is sent. Only when this method returns true can a backup be assumed + * to have succeeded. + * + * @return false if errors occurred (the backup should be aborted and rescheduled), + * true if everything is OK. + */ boolean finishBackup(); /** diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index c5d9d40..2facce2 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -111,6 +111,17 @@ public class LocalTransport extends IBackupTransport.Stub { } } + public boolean clearBackupData(PackageInfo packageInfo) { + if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); + + File packageDir = new File(mDataDir, packageInfo.packageName); + for (File f : packageDir.listFiles()) { + f.delete(); + } + packageDir.delete(); + return true; + } + public boolean finishBackup() throws RemoteException { if (DEBUG) Log.v(TAG, "finishBackup()"); return true; diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 953e401..8790472 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -81,6 +81,7 @@ class BackupManagerService extends IBackupManager.Stub { private static final int MSG_RUN_BACKUP = 1; private static final int MSG_RUN_FULL_BACKUP = 2; private static final int MSG_RUN_RESTORE = 3; + private static final int MSG_RUN_CLEAR = 4; // Timeout interval for deciding that a bind or clear-data has taken too long static final long TIMEOUT_INTERVAL = 10 * 1000; @@ -148,6 +149,16 @@ class BackupManagerService extends IBackupManager.Stub { } } + private class ClearParams { + public IBackupTransport transport; + public PackageInfo packageInfo; + + ClearParams(IBackupTransport _transport, PackageInfo _info) { + transport = _transport; + packageInfo = _info; + } + } + // Where we keep our journal files and other bookkeeping private File mBaseStateDir; private File mDataDir; @@ -386,6 +397,13 @@ class BackupManagerService extends IBackupManager.Stub { (new PerformRestoreThread(params.transport, params.observer, params.token)).start(); break; } + + case MSG_RUN_CLEAR: + { + ClearParams params = (ClearParams)msg.obj; + (new PerformClearThread(params.transport, params.packageInfo)).start(); + break; + } } } } @@ -1071,6 +1089,37 @@ class BackupManagerService extends IBackupManager.Stub { } } + class PerformClearThread extends Thread { + IBackupTransport mTransport; + PackageInfo mPackage; + + PerformClearThread(IBackupTransport transport, PackageInfo packageInfo) { + mTransport = transport; + mPackage = packageInfo; + } + + @Override + public void run() { + try { + // Clear the on-device backup state to ensure a full backup next time + File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); + File stateFile = new File(stateDir, mPackage.packageName); + stateFile.delete(); + + // Tell the transport to remove all the persistent storage for the app + mTransport.clearBackupData(mPackage); + } catch (RemoteException e) { + // can't happen; the transport is local + } finally { + try { + mTransport.finishBackup(); + } catch (RemoteException e) { + // can't happen; the transport is local + } + } + } + } + // ----- IBackupManager binder interface ----- @@ -1142,6 +1191,52 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Clear the given package's backup data from the current transport + public void clearBackupData(String packageName) { + if (DEBUG) Log.v(TAG, "clearBackupData() of " + packageName); + PackageInfo info; + try { + info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + Log.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); + return; + } + + // If the caller does not hold the BACKUP permission, it can only request a + // wipe of its own backed-up data. + HashSet<ApplicationInfo> apps; + if ((mContext.checkPermission("android.permission.BACKUP", Binder.getCallingPid(), + Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { + apps = mBackupParticipants.get(Binder.getCallingUid()); + } else { + // a caller with full permission can ask to back up any participating app + // !!! TODO: allow data-clear of ANY app? + if (DEBUG) Log.v(TAG, "Privileged caller, allowing clear of other apps"); + apps = new HashSet<ApplicationInfo>(); + int N = mBackupParticipants.size(); + for (int i = 0; i < N; i++) { + HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i); + if (s != null) { + apps.addAll(s); + } + } + } + + // now find the given package in the set of candidate apps + for (ApplicationInfo app : apps) { + if (app.packageName.equals(packageName)) { + if (DEBUG) Log.v(TAG, "Found the app - running clear process"); + // found it; fire off the clear request + synchronized (mQueueLock) { + Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, + new ClearParams(getTransport(mCurrentTransport), info)); + mBackupHandler.sendMessage(msg); + } + break; + } + } + } + // Run a backup pass immediately for any applications that have declared // that they have pending updates. public void backupNow() throws RemoteException { |