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