diff options
-rw-r--r-- | cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java | 32 | ||||
-rw-r--r-- | core/java/android/app/backup/IRestoreSession.aidl | 19 | ||||
-rw-r--r-- | core/java/android/app/backup/RestoreSession.java | 34 | ||||
-rw-r--r-- | services/java/com/android/server/BackupManagerService.java | 108 |
4 files changed, 184 insertions, 9 deletions
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 38d0d2a..2666b41 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -23,6 +23,8 @@ import android.app.backup.IRestoreSession; import android.os.RemoteException; import android.os.ServiceManager; +import java.util.HashSet; + public final class Bmgr { IBackupManager mBmgr; IRestoreSession mRestore; @@ -45,7 +47,6 @@ public final class Bmgr { } public void run(String[] args) { - boolean validCommand = false; if (args.length < 1) { showUsage(); return; @@ -329,7 +330,13 @@ public final class Bmgr { } else { try { long token = Long.parseLong(arg, 16); - doRestoreAll(token); + HashSet<String> filter = null; + while ((arg = nextArg()) != null) { + if (filter == null) filter = new HashSet<String>(); + filter.add(arg); + } + + doRestoreAll(token, filter); } catch (NumberFormatException e) { showUsage(); return; @@ -364,7 +371,7 @@ public final class Bmgr { } } - private void doRestoreAll(long token) { + private void doRestoreAll(long token, HashSet<String> filter) { RestoreObserver observer = new RestoreObserver(); try { @@ -383,7 +390,13 @@ public final class Bmgr { for (RestoreSet s : sets) { if (s.token == token) { System.out.println("Scheduling restore: " + s.name); - didRestore = (mRestore.restoreAll(token, observer) == 0); + if (filter == null) { + didRestore = (mRestore.restoreAll(token, observer) == 0); + } else { + String[] names = new String[filter.size()]; + filter.toArray(names); + didRestore = (mRestore.restoreSome(token, observer, names) == 0); + } break; } } @@ -430,6 +443,7 @@ public final class Bmgr { System.err.println(" bmgr list sets"); System.err.println(" bmgr transport WHICH"); System.err.println(" bmgr restore TOKEN"); + System.err.println(" bmgr restore TOKEN PACKAGE..."); System.err.println(" bmgr restore PACKAGE"); System.err.println(" bmgr run"); System.err.println(" bmgr wipe PACKAGE"); @@ -457,12 +471,18 @@ public final class Bmgr { System.err.println("The 'transport' command designates the named transport as the currently"); System.err.println("active one. This setting is persistent across reboots."); System.err.println(""); - System.err.println("The 'restore' command when given a restore token initiates a full-system"); + System.err.println("The 'restore' command when given just a restore token initiates a full-system"); System.err.println("restore operation from the currently active transport. It will deliver"); System.err.println("the restore set designated by the TOKEN argument to each application"); System.err.println("that had contributed data to that restore set."); System.err.println(""); - System.err.println("The 'restore' command when given a package name intiates a restore of"); + System.err.println("The 'restore' command when given a token and one or more package names"); + System.err.println("initiates a restore operation of just those given packages from the restore"); + System.err.println("set designated by the TOKEN argument. It is effectively the same as the"); + System.err.println("'restore' operation supplying only a token, but applies a filter to the"); + System.err.println("set of applications to be restored."); + System.err.println(""); + System.err.println("The 'restore' command when given just a package name intiates a restore of"); System.err.println("just that one package according to the restore set selection algorithm"); System.err.println("used by the RestoreSession.restorePackage() method."); System.err.println(""); diff --git a/core/java/android/app/backup/IRestoreSession.aidl b/core/java/android/app/backup/IRestoreSession.aidl index 1dddbb0..14731ee 100644 --- a/core/java/android/app/backup/IRestoreSession.aidl +++ b/core/java/android/app/backup/IRestoreSession.aidl @@ -52,6 +52,25 @@ interface IRestoreSession { int restoreAll(long token, IRestoreObserver observer); /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + */ + int restoreSome(long token, IRestoreObserver observer, in String[] packages); + + /** * Restore a single application from backup. The data will be restored from the * current backup dataset if the given package has stored data there, or from * the dataset used during the last full device setup operation if the current diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java index 24ddb99..7181c61 100644 --- a/core/java/android/app/backup/RestoreSession.java +++ b/core/java/android/app/backup/RestoreSession.java @@ -87,6 +87,40 @@ public class RestoreSession { } /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + * + * @hide + */ + public int restoreSome(long token, RestoreObserver observer, String[] packages) { + int err = -1; + if (mObserver != null) { + Log.d(TAG, "restoreAll() called during active restore"); + return -1; + } + mObserver = new RestoreObserverWrapper(mContext, observer); + try { + err = mBinder.restoreSome(token, mObserver, packages); + } catch (RemoteException e) { + Log.d(TAG, "Can't contact server to restore packages"); + } + return err; + } + + /** * Restore a single application from backup. The data will be restored from the * current backup dataset if the given package has stored data there, or from * the dataset used during the last full device setup operation if the current diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 6afccec..786f2fa 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -224,6 +224,7 @@ class BackupManagerService extends IBackupManager.Stub { public PackageInfo pkgInfo; public int pmToken; // in post-install restore, the PM's token for this transaction public boolean needFullBackup; + public String[] filterSet; RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) { @@ -233,6 +234,7 @@ class BackupManagerService extends IBackupManager.Stub { pkgInfo = _pkg; pmToken = _pmToken; needFullBackup = _needFullBackup; + filterSet = null; } RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, @@ -243,6 +245,18 @@ class BackupManagerService extends IBackupManager.Stub { pkgInfo = null; pmToken = 0; needFullBackup = _needFullBackup; + filterSet = null; + } + + RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, + String[] _filterSet, boolean _needFullBackup) { + transport = _transport; + observer = _obs; + token = _token; + pkgInfo = null; + pmToken = 0; + needFullBackup = _needFullBackup; + filterSet = _filterSet; } } @@ -404,7 +418,7 @@ class BackupManagerService extends IBackupManager.Stub { Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); (new PerformRestoreTask(params.transport, params.observer, params.token, params.pkgInfo, params.pmToken, - params.needFullBackup)).run(); + params.needFullBackup, params.filterSet)).run(); break; } @@ -3020,6 +3034,7 @@ class BackupManagerService extends IBackupManager.Stub { private File mStateDir; private int mPmToken; private boolean mNeedFullBackup; + private HashSet<String> mFilterSet; class RestoreRequest { public PackageInfo app; @@ -3033,7 +3048,7 @@ class BackupManagerService extends IBackupManager.Stub { PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer, long restoreSetToken, PackageInfo targetPackage, int pmToken, - boolean needFullBackup) { + boolean needFullBackup, String[] filterSet) { mTransport = transport; mObserver = observer; mToken = restoreSetToken; @@ -3041,6 +3056,15 @@ class BackupManagerService extends IBackupManager.Stub { mPmToken = pmToken; mNeedFullBackup = needFullBackup; + if (filterSet != null) { + mFilterSet = new HashSet<String>(); + for (String pkg : filterSet) { + mFilterSet.add(pkg); + } + } else { + mFilterSet = null; + } + try { mStateDir = new File(mBaseStateDir, transport.transportDirName()); } catch (RemoteException e) { @@ -3052,7 +3076,8 @@ class BackupManagerService extends IBackupManager.Stub { long startRealtime = SystemClock.elapsedRealtime(); if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken) - + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken); + + " mTargetPackage=" + mTargetPackage + " mFilterSet=" + mFilterSet + + " mPmToken=" + mPmToken); PackageManagerBackupAgent pmAgent = null; int error = -1; // assume error @@ -3071,6 +3096,22 @@ class BackupManagerService extends IBackupManager.Stub { List<PackageInfo> agentPackages = allAgentPackages(); if (mTargetPackage == null) { + // if there's a filter set, strip out anything that isn't + // present before proceeding + if (mFilterSet != null) { + for (int i = agentPackages.size() - 1; i >= 0; i--) { + final PackageInfo pkg = agentPackages.get(i); + if (! mFilterSet.contains(pkg.packageName)) { + agentPackages.remove(i); + } + } + if (DEBUG) { + Slog.i(TAG, "Post-filter package set for restore:"); + for (PackageInfo p : agentPackages) { + Slog.i(TAG, " " + p); + } + } + } restorePackages.addAll(agentPackages); } else { // Just one package to attempt restore of @@ -4266,6 +4307,67 @@ class BackupManagerService extends IBackupManager.Stub { return -1; } + public synchronized int restoreSome(long token, IRestoreObserver observer, + String[] packages) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "performRestore"); + + if (DEBUG) { + StringBuilder b = new StringBuilder(128); + b.append("restoreSome token="); + b.append(Long.toHexString(token)); + b.append(" observer="); + b.append(observer.toString()); + b.append(" packages="); + if (packages == null) { + b.append("null"); + } else { + b.append('{'); + boolean first = true; + for (String s : packages) { + if (!first) { + b.append(", "); + } else first = false; + b.append(s); + } + b.append('}'); + } + Slog.d(TAG, b.toString()); + } + + if (mEnded) { + throw new IllegalStateException("Restore session already ended"); + } + + if (mRestoreTransport == null || mRestoreSets == null) { + Slog.e(TAG, "Ignoring restoreAll() with no restore set"); + return -1; + } + + if (mPackageName != null) { + Slog.e(TAG, "Ignoring restoreAll() on single-package session"); + return -1; + } + + synchronized (mQueueLock) { + for (int i = 0; i < mRestoreSets.length; i++) { + if (token == mRestoreSets[i].token) { + long oldId = Binder.clearCallingIdentity(); + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(mRestoreTransport, observer, token, + packages, true); + mBackupHandler.sendMessage(msg); + Binder.restoreCallingIdentity(oldId); + return 0; + } + } + } + + Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); + return -1; + } + public synchronized int restorePackage(String packageName, IRestoreObserver observer) { if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); |