Add facility for broadcasts receives to do work asynchronously.

You can now call goAsync() and move your work to a background thread.
If you are that kind of receiver.  You weirdo.

Also allows SharedPreferences.apply() to be committed off the main
thread after returning from onReceive().

Change-Id: I27f975910e28f230ababcaeb551eb9a78ec4fc76
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c5badaf..754295a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -117,12 +117,14 @@
  * {@hide}
  */
 public final class ActivityThread {
-    static final String TAG = "ActivityThread";
+    /** @hide */
+    public static final String TAG = "ActivityThread";
     private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
     private static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
     static final boolean DEBUG_MESSAGES = false;
-    static final boolean DEBUG_BROADCAST = false;
+    /** @hide */
+    public static final boolean DEBUG_BROADCAST = false;
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = false;
     private static final boolean DEBUG_CONFIGURATION = false;
@@ -262,18 +264,20 @@
         }
     }
 
-    private static final class ReceiverData {
+    private static final class ReceiverData extends BroadcastReceiver.PendingResult {
+        public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
+                boolean ordered, boolean sticky, IBinder token) {
+            super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
+            this.intent = intent;
+        }
+        
         Intent intent;
         ActivityInfo info;
-        int resultCode;
-        String resultData;
-        Bundle resultExtras;
-        boolean sync;
-        boolean resultAbort;
         public String toString() {
             return "ReceiverData{intent=" + intent + " packageName=" +
-            info.packageName + " resultCode=" + resultCode
-            + " resultData=" + resultData + " resultExtras=" + resultExtras + "}";
+                    info.packageName + " resultCode=" + getResultCode()
+                    + " resultData=" + getResultData() + " resultExtras="
+                    + getResultExtras(false) + "}";
         }
     }
 
@@ -466,15 +470,9 @@
 
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
                 int resultCode, String data, Bundle extras, boolean sync) {
-            ReceiverData r = new ReceiverData();
-
-            r.intent = intent;
+            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
+                    sync, false, mAppThread.asBinder());
             r.info = info;
-            r.resultCode = resultCode;
-            r.resultData = data;
-            r.resultExtras = extras;
-            r.sync = sync;
-
             queueOrSendMessage(H.RECEIVER, r);
         }
 
@@ -1799,18 +1797,12 @@
         try {
             java.lang.ClassLoader cl = packageInfo.getClassLoader();
             data.intent.setExtrasClassLoader(cl);
-            if (data.resultExtras != null) {
-                data.resultExtras.setClassLoader(cl);
-            }
+            data.setExtrasClassLoader(cl);
             receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
         } catch (Exception e) {
-            try {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing failed broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
-                                   data.resultData, data.resultExtras, data.resultAbort);
-            } catch (RemoteException ex) {
-            }
+            if (DEBUG_BROADCAST) Slog.i(TAG,
+                    "Finishing failed broadcast to " + data.intent.getComponent());
+            data.sendFinished(mgr);
             throw new RuntimeException(
                 "Unable to instantiate receiver " + component
                 + ": " + e.toString(), e);
@@ -1828,20 +1820,13 @@
                 + ", dir=" + packageInfo.getAppDir());
 
             ContextImpl context = (ContextImpl)app.getBaseContext();
-            receiver.setOrderedHint(true);
-            receiver.setResult(data.resultCode, data.resultData,
-                data.resultExtras);
-            receiver.setOrderedHint(data.sync);
+            receiver.setPendingResult(data);
             receiver.onReceive(context.getReceiverRestrictedContext(),
                     data.intent);
         } catch (Exception e) {
-            try {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing failed broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
-                    data.resultData, data.resultExtras, data.resultAbort);
-            } catch (RemoteException ex) {
-            }
+            if (DEBUG_BROADCAST) Slog.i(TAG,
+                    "Finishing failed broadcast to " + data.intent.getComponent());
+            data.sendFinished(mgr);
             if (!mInstrumentation.onException(receiver, e)) {
                 throw new RuntimeException(
                     "Unable to start receiver " + component
@@ -1849,22 +1834,8 @@
             }
         }
 
-        QueuedWork.waitToFinish();
-
-        try {
-            if (data.sync) {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing ordered broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(
-                    mAppThread.asBinder(), receiver.getResultCode(),
-                    receiver.getResultData(), receiver.getResultExtras(false),
-                        receiver.getAbortBroadcast());
-            } else {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(mAppThread.asBinder(), 0, null, null, false);
-            }
-        } catch (RemoteException ex) {
+        if (receiver.getPendingResult() != null) {
+            data.finish();
         }
     }