Move towards a formal public API for backup and restore
This commit makes a few changes towards establishing a formal application
interface for interacting with the backup/restore mechanism:
1. Introduce public wrapper classes around the various binder interfaces; 3rd
party code will never see the binders directly.
2. Progress update callbacks during a restore sequence now occur on the main
thread, not in a binder thread [and not with system-process permissions!].
3. Rename the BackupManagerService's inner "RestoreSession" class to avoid
ambiguity with the new public "RestoreSession" class.
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index da1647a..0b27117 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -16,6 +16,7 @@
package android.backup;
+import android.backup.RestoreSession;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -115,19 +116,21 @@
*
* {@hide}
*/
- public IRestoreSession beginRestoreSession(String transport) {
+ public RestoreSession beginRestoreSession() {
if (!EVEN_THINK_ABOUT_DOING_RESTORE) {
return null;
}
- IRestoreSession binder = null;
+ RestoreSession session = null;
checkServiceBinder();
if (sService != null) {
try {
- binder = sService.beginRestoreSession(transport);
+ String transport = sService.getCurrentTransport();
+ IRestoreSession binder = sService.beginRestoreSession(transport);
+ session = new RestoreSession(mContext, binder);
} catch (RemoteException e) {
Log.d(TAG, "beginRestoreSession() couldn't connect");
}
}
- return binder;
+ return session;
}
}
diff --git a/core/java/android/backup/RestoreObserver.java b/core/java/android/backup/RestoreObserver.java
new file mode 100644
index 0000000..3be8c08
--- /dev/null
+++ b/core/java/android/backup/RestoreObserver.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.backup;
+
+/**
+ * Callback class for receiving progress reports during a restore operation. These
+ * methods will all be called on your application's main thread.
+ * @hide
+ */
+public abstract class RestoreObserver {
+ /**
+ * The restore operation has begun.
+ *
+ * @param numPackages The total number of packages being processed in
+ * this restore operation.
+ */
+ void restoreStarting(int numPackages) {
+ }
+
+ /**
+ * An indication of which package is being restored currently, out of the
+ * total number provided in the restoreStarting() callback. This method
+ * is not guaranteed to be called.
+ *
+ * @param nowBeingRestored The index, between 1 and the numPackages parameter
+ * to the restoreStarting() callback, of the package now being restored.
+ */
+ void onUpdate(int nowBeingRestored) {
+ }
+
+ /**
+ * The restore operation has completed.
+ *
+ * @param error Zero on success; a nonzero error code if the restore operation
+ * as a whole failed.
+ */
+ void restoreFinished(int error) {
+ }
+}
diff --git a/core/java/android/backup/RestoreSession.java b/core/java/android/backup/RestoreSession.java
new file mode 100644
index 0000000..119fc52
--- /dev/null
+++ b/core/java/android/backup/RestoreSession.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.backup;
+
+import android.backup.IRestoreSession;
+import android.backup.RestoreObserver;
+import android.backup.RestoreSet;
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Interface for applications to use when managing a restore session.
+ * @hide
+ */
+public class RestoreSession {
+ static final String TAG = "RestoreSession";
+
+ final Context mContext;
+ IRestoreSession mBinder;
+ RestoreObserverWrapper mObserver = null;
+
+ /**
+ * Ask the current transport what the available restore sets are.
+ *
+ * @return A bundle containing two elements: an int array under the key
+ * "tokens" whose entries are a transport-private identifier for each backup set;
+ * and a String array under the key "names" whose entries are the user-meaningful
+ * text corresponding to the backup sets at each index in the tokens array.
+ * On error, returns null.
+ */
+ public RestoreSet[] getAvailableRestoreSets() {
+ try {
+ return mBinder.getAvailableRestoreSets();
+ } catch (RemoteException e) {
+ Log.d(TAG, "Can't contact server to get available sets");
+ return null;
+ }
+ }
+
+ /**
+ * Restore the given set onto the device, replacing the current data of any app
+ * contained in the restore set with the data previously backed up.
+ *
+ * @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 argument points to an object that will receive
+ * progress callbacks during the restore operation. These callbacks will occur
+ * on the main thread of the application.
+ */
+ public int performRestore(long token, RestoreObserver observer) {
+ int err = -1;
+ if (mObserver != null) {
+ Log.d(TAG, "performRestore() called during active restore");
+ return -1;
+ }
+ mObserver = new RestoreObserverWrapper(mContext, observer);
+ try {
+ err = mBinder.performRestore(token, mObserver);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Can't contact server to perform restore");
+ }
+ return err;
+ }
+
+ /**
+ * End this restore session. After this method is called, the RestoreSession
+ * object is no longer valid.
+ *
+ * <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
+ * even if {@link #getAvailableRestoreSets()} or
+ * {@link #performRestore(long, RestoreObserver)} failed.
+ */
+ public void endRestoreSession() {
+ try {
+ mBinder.endRestoreSession();
+ } catch (RemoteException e) {
+ Log.d(TAG, "Can't contact server to get available sets");
+ } finally {
+ mBinder = null;
+ }
+ }
+
+ /*
+ * Nonpublic implementation here
+ */
+
+ RestoreSession(Context context, IRestoreSession binder) {
+ mContext = context;
+ mBinder = binder;
+ }
+
+ /*
+ * We wrap incoming binder calls with a private class implementation that
+ * redirects them into main-thread actions. This accomplishes two things:
+ * first, it ensures that the app's code is run on their own main thread,
+ * never with system Binder identity; and second, it serializes the restore
+ * progress callbacks nicely within the usual main-thread lifecycle pattern.
+ */
+ private class RestoreObserverWrapper extends IRestoreObserver.Stub {
+ final Handler mHandler;
+ final RestoreObserver mAppObserver;
+
+ RestoreObserverWrapper(Context context, RestoreObserver appObserver) {
+ mHandler = new Handler(context.getMainLooper());
+ mAppObserver = appObserver;
+ }
+
+ // Wrap the IRestoreObserver -> RestoreObserver callthrough in Runnables
+ // posted to the app's main thread looper.
+ class RestoreStartingRunnable implements Runnable {
+ int mNumPackages;
+
+ RestoreStartingRunnable(int numPackages) {
+ mNumPackages = numPackages;
+ }
+
+ public void run() {
+ mAppObserver.restoreStarting(mNumPackages);
+ }
+ }
+
+ class OnUpdateRunnable implements Runnable {
+ int mNowRestoring;
+
+ OnUpdateRunnable(int nowRestoring) {
+ mNowRestoring = nowRestoring;
+ }
+
+ public void run() {
+ mAppObserver.onUpdate(mNowRestoring);
+ }
+ }
+
+ class RestoreFinishedRunnable implements Runnable {
+ int mError;
+
+ RestoreFinishedRunnable(int error) {
+ mError = error;
+ }
+
+ public void run() {
+ mAppObserver.restoreFinished(mError);
+ }
+ }
+
+ // The actual redirection code is quite simple using just the
+ // above Runnable subclasses
+ public void restoreStarting(int numPackages) {
+ mHandler.post(new RestoreStartingRunnable(numPackages));
+ }
+
+ public void onUpdate(int nowBeingRestored) {
+ mHandler.post(new OnUpdateRunnable(nowBeingRestored));
+ }
+
+ public void restoreFinished(int error) {
+ mHandler.post(new RestoreFinishedRunnable(error));
+ }
+ }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2e45512..72e26f8 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -161,7 +161,7 @@
= new HashMap<String,IBackupTransport>();
String mCurrentTransport;
IBackupTransport mLocalTransport, mGoogleTransport;
- RestoreSession mActiveRestoreSession;
+ ActiveRestoreSession mActiveRestoreSession;
class RestoreParams {
public IBackupTransport transport;
@@ -2068,20 +2068,20 @@
Log.d(TAG, "Restore session requested but one already active");
return null;
}
- mActiveRestoreSession = new RestoreSession(transport);
+ mActiveRestoreSession = new ActiveRestoreSession(transport);
}
return mActiveRestoreSession;
}
// ----- Restore session -----
- class RestoreSession extends IRestoreSession.Stub {
+ class ActiveRestoreSession extends IRestoreSession.Stub {
private static final String TAG = "RestoreSession";
private IBackupTransport mRestoreTransport = null;
RestoreSet[] mRestoreSets = null;
- RestoreSession(String transport) {
+ ActiveRestoreSession(String transport) {
mRestoreTransport = getTransport(transport);
}