Make RestoreSession.getAvailableRestoreSets() asynchronous

This transaction can involve the transport having to query a remote backend
over the wire, so it can take a Long Time(tm).  Make it main-thread-safe by
making it asynchronous, with the results passed as a callback to the invoker's
RestoreObserver.  We also make the IRestoreObserver callback interface
properly oneway.

Bug #2550665
Bug #2549422

Change-Id: If18a233a0a3d54c7b55101715c9e6195b762c5a0
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6c1fa60..ecf1a1d 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -104,7 +104,8 @@
     private static final int MSG_RUN_RESTORE = 3;
     private static final int MSG_RUN_CLEAR = 4;
     private static final int MSG_RUN_INITIALIZE = 5;
-    private static final int MSG_TIMEOUT = 6;
+    private static final int MSG_RUN_GET_RESTORE_SETS = 6;
+    private static final int MSG_TIMEOUT = 7;
 
     // Timeout interval for deciding that a bind or clear-data has taken too long
     static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -177,6 +178,19 @@
     IBackupTransport mLocalTransport, mGoogleTransport;
     ActiveRestoreSession mActiveRestoreSession;
 
+    class RestoreGetSetsParams {
+        public IBackupTransport transport;
+        public ActiveRestoreSession session;
+        public IRestoreObserver observer;
+
+        RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
+                IRestoreObserver _observer) {
+            transport = _transport;
+            session = _session;
+            observer = _observer;
+        }
+    }
+
     class RestoreParams {
         public IBackupTransport transport;
         public IRestoreObserver observer;
@@ -333,6 +347,36 @@
                 break;
             }
 
+            case MSG_RUN_GET_RESTORE_SETS:
+            {
+                // Like other async operations, this is entered with the wakelock held
+                RestoreSet[] sets = null;
+                RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
+                try {
+                    sets = params.transport.getAvailableRestoreSets();
+                    // cache the result in the active session
+                    synchronized (params.session) {
+                        params.session.mRestoreSets = sets;
+                    }
+                    if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Error from transport getting set list");
+                } finally {
+                    if (params.observer != null) {
+                        try {
+                            params.observer.restoreSetsAvailable(sets);
+                        } catch (RemoteException re) {
+                            Slog.e(TAG, "Unable to report listing to observer");
+                        } catch (Exception e) {
+                            Slog.e(TAG, "Restore observer threw", e);
+                        }
+                    }
+
+                    mWakelock.release();
+                }
+                break;
+            }
+
             case MSG_TIMEOUT:
             {
                 synchronized (mCurrentOpLock) {
@@ -2343,24 +2387,28 @@
         }
 
         // --- Binder interface ---
-        public synchronized RestoreSet[] getAvailableRestoreSets() {
+        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                     "getAvailableRestoreSets");
+            if (observer == null) {
+                throw new IllegalArgumentException("Observer must not be null");
+            }
 
             long oldId = Binder.clearCallingIdentity();
             try {
                 if (mRestoreTransport == null) {
                     Slog.w(TAG, "Null transport getting restore sets");
-                    return null;
+                    return -1;
                 }
-                if (mRestoreSets == null) { // valid transport; do the one-time fetch
-                    mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
-                    if (mRestoreSets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
-                }
-                return mRestoreSets;
+                // spin off the transport request to our service thread
+                mWakelock.acquire();
+                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
+                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
+                mBackupHandler.sendMessage(msg);
+                return 0;
             } catch (Exception e) {
                 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
-                return null;
+                return -1;
             } finally {
                 Binder.restoreCallingIdentity(oldId);
             }