am 7d562ec3: Add a new IRestoreObserver callback class to the restore process

Merge commit '7d562ec393d54dd9ef387c49d1283243bfdbd2b1'

* commit '7d562ec393d54dd9ef387c49d1283243bfdbd2b1':
  Add a new IRestoreObserver callback class to the restore process
diff --git a/Android.mk b/Android.mk
index 58b149e..01aef7a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -85,6 +85,7 @@
 	core/java/android/app/IWallpaperService.aidl \
 	core/java/android/app/IWallpaperServiceCallback.aidl \
 	core/java/android/backup/IBackupManager.aidl \
+	core/java/android/backup/IRestoreObserver.aidl \
 	core/java/android/backup/IRestoreSession.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl
new file mode 100644
index 0000000..59e59fc
--- /dev/null
+++ b/core/java/android/backup/IRestoreObserver.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ * @hide
+ */
+interface IRestoreObserver {
+    /**
+     * 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/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index 6bca865..ac01c2d 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -17,6 +17,7 @@
 package android.backup;
 
 import android.backup.RestoreSet;
+import android.backup.IRestoreObserver;
 
 /**
  * Binder interface used by clients who wish to manage a restore operation.  Every
@@ -41,8 +42,10 @@
      *
      * @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.
      */
-    int performRestore(int token);
+    int performRestore(int token, IRestoreObserver observer);
 
     /**
      * End this restore session.  After this method is called, the IRestoreSession binder
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index e131c6eb..bc2eaed 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -47,6 +47,7 @@
 import android.util.SparseArray;
 
 import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
 import android.backup.IRestoreSession;
 import android.backup.BackupManager;
 import android.backup.RestoreSet;
@@ -81,6 +82,7 @@
     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 String RESTORE_OBSERVER_KEY = "_resOb";
 
     // Timeout interval for deciding that a bind or clear-data has taken too long
     static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -133,6 +135,16 @@
     private IBackupTransport mLocalTransport, mGoogleTransport;
     private RestoreSession mActiveRestoreSession;
 
+    private class RestoreParams {
+        public IBackupTransport transport;
+        public IRestoreObserver observer;
+
+        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs) {
+            transport = _transport;
+            observer = _obs;
+        }
+    }
+
     // Where we keep our journal files and other bookkeeping
     private File mBaseStateDir;
     private File mDataDir;
@@ -336,8 +348,8 @@
             case MSG_RUN_RESTORE:
             {
                 int token = msg.arg1;
-                IBackupTransport transport = (IBackupTransport)msg.obj;
-                (new PerformRestoreThread(transport, token)).start();
+                RestoreParams params = (RestoreParams)msg.obj;
+                (new PerformRestoreThread(params.transport, params.observer, token)).start();
                 break;
             }
             }
@@ -748,6 +760,7 @@
 
     class PerformRestoreThread extends Thread {
         private IBackupTransport mTransport;
+        private IRestoreObserver mObserver;
         private int mToken;
         private RestoreSet mImage;
         private File mStateDir;
@@ -762,8 +775,10 @@
             }
         }
 
-        PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
+        PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
+                int restoreSetToken) {
             mTransport = transport;
+            mObserver = observer;
             mToken = restoreSetToken;
 
             try {
@@ -792,6 +807,8 @@
              * 4. shut down the transport
              */
 
+            int error = -1; // assume error
+
             // build the set of apps to restore
             try {
                 RestoreSet[] images = mTransport.getAvailableRestoreSets();
@@ -818,6 +835,18 @@
                 List<PackageInfo> agentPackages = allAgentPackages();
                 restorePackages.addAll(agentPackages);
 
+                // let the observer know that we're running
+                if (mObserver != null) {
+                    try {
+                        // !!! TODO: get an actual count from the transport after
+                        // its startRestore() runs?
+                        mObserver.restoreStarting(restorePackages.size());
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreStarting");
+                        mObserver = null;
+                    }
+                }
+
                 // STOPSHIP TODO: pick out the set for this token (instead of images[0])
                 long token = images[0].token;
                 if (!mTransport.startRestore(token, restorePackages.toArray(new PackageInfo[0]))) {
@@ -845,6 +874,7 @@
                         mPackageManager, agentPackages);
                 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
 
+                int count = 0;
                 for (;;) {
                     packageName = mTransport.nextRestorePackage();
                     if (packageName == null) {
@@ -855,6 +885,16 @@
                         break;
                     }
 
+                    if (mObserver != null) {
+                        ++count;
+                        try {
+                            mObserver.onUpdate(count);
+                        } catch (RemoteException e) {
+                            Log.d(TAG, "Restore observer died in onUpdate");
+                            mObserver = null;
+                        }
+                    }
+
                     Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
                     if (metaInfo == null) {
                         Log.e(TAG, "Missing metadata for " + packageName);
@@ -898,6 +938,9 @@
                         mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
                     }
                 }
+
+                // if we get this far, report success to the observer
+                error = 0;
             } catch (NameNotFoundException e) {
                 // STOPSHIP TODO: Handle the failure somehow?
                 Log.e(TAG, "Invalid paackage restoring data", e);
@@ -910,6 +953,14 @@
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error finishing restore", e);
                 }
+
+                if (mObserver != null) {
+                    try {
+                        mObserver.restoreFinished(error);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreFinished");
+                    }
+                }
             }
         }
 
@@ -1147,14 +1198,15 @@
             }
         }
 
-        public int performRestore(int token) throws android.os.RemoteException {
+        public int performRestore(int token, IRestoreObserver observer)
+                throws android.os.RemoteException {
             mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
 
             if (mRestoreSets != null) {
                 for (int i = 0; i < mRestoreSets.length; i++) {
                     if (token == mRestoreSets[i].token) {
-                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
-                                mRestoreTransport);
+                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                        msg.obj = new RestoreParams(mRestoreTransport, observer);
                         msg.arg1 = token;
                         mBackupHandler.sendMessage(msg);
                         return 0;