Fix issue #2166755: BroadcastReceiver trying to return result during a non-ordered broadcast

Tell the broadcast receiver whether it is getting an initial sticky value,
so it will be quiet about attempts to do ordered broadcast stuff.

Note that the original bug being reported was not actually a crash, just
an error log.  So all we are doing here is making the log quieter.

Change-Id: Iaf1b718d82093ec1197142410a64feff47eb3859
diff --git a/api/current.xml b/api/current.xml
index e764fa5..9b0ea3a 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -27787,6 +27787,28 @@
 <parameter name="makeMap" type="boolean">
 </parameter>
 </method>
+<method name="isInitialStickyBroadcast"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isOrderedBroadcast"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="onReceive"
  return="void"
  abstract="true"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 0b4f25e..eca5af9 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -340,7 +340,8 @@
         private boolean mFinished = false;
 
         public synchronized void performReceive(
-                Intent intent, int rc, String data, Bundle ext, boolean ord) {
+                Intent intent, int rc, String data, Bundle ext, boolean ord,
+                boolean sticky) {
             String line = "Broadcast completed: result=" + rc;
             if (data != null) line = line + ", data=\"" + data + "\"";
             if (ext != null) line = line + ", extras: " + ext;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b4ac159..2cd223f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -653,7 +653,7 @@
                     mStrongRef = strong ? rd : null;
                 }
                 public void performReceive(Intent intent, int resultCode,
-                        String data, Bundle extras, boolean ordered) {
+                        String data, Bundle extras, boolean ordered, boolean sticky) {
                     ReceiverDispatcher rd = mDispatcher.get();
                     if (DEBUG_BROADCAST) {
                         int seq = intent.getIntExtra("seq", -1);
@@ -661,7 +661,8 @@
                                 + " to " + rd);
                     }
                     if (rd != null) {
-                        rd.performReceive(intent, resultCode, data, extras, ordered);
+                        rd.performReceive(intent, resultCode, data, extras,
+                                ordered, sticky);
                     }
                 }
             }
@@ -681,6 +682,7 @@
                 private String mCurData;
                 private Bundle mCurMap;
                 private boolean mCurOrdered;
+                private boolean mCurSticky;
 
                 public void run() {
                     BroadcastReceiver receiver = mReceiver;
@@ -706,6 +708,7 @@
                         receiver.setResult(mCurCode, mCurData, mCurMap);
                         receiver.clearAbortBroadcast();
                         receiver.setOrderedHint(mCurOrdered);
+                        receiver.setInitialStickyHint(mCurSticky);
                         receiver.onReceive(mContext, intent);
                     } catch (Exception e) {
                         if (mRegistered && mCurOrdered) {
@@ -788,7 +791,7 @@
             }
 
             public void performReceive(Intent intent, int resultCode,
-                    String data, Bundle extras, boolean ordered) {
+                    String data, Bundle extras, boolean ordered, boolean sticky) {
                 if (DEBUG_BROADCAST) {
                     int seq = intent.getIntExtra("seq", -1);
                     Log.i(TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
@@ -800,6 +803,7 @@
                 args.mCurData = data;
                 args.mCurMap = extras;
                 args.mCurOrdered = ordered;
+                args.mCurSticky = sticky;
                 if (!mActivityThread.post(args)) {
                     if (mRegistered) {
                         IActivityManager mgr = ActivityManagerNative.getDefault();
@@ -1515,9 +1519,9 @@
         // correctly ordered, since these are one-way calls and the binder driver
         // applies transaction ordering per object for such calls.
         public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
-                int resultCode, String dataStr, Bundle extras, boolean ordered)
-                throws RemoteException {
-            receiver.performReceive(intent, resultCode, dataStr, extras, ordered);
+                int resultCode, String dataStr, Bundle extras, boolean ordered,
+                boolean sticky) throws RemoteException {
+            receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
         }
 
         public void scheduleLowMemory() {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 928981d..a772a8f 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -317,8 +317,9 @@
             String dataStr = data.readString();
             Bundle extras = data.readBundle();
             boolean ordered = data.readInt() != 0;
+            boolean sticky = data.readInt() != 0;
             scheduleRegisteredReceiver(receiver, intent,
-                    resultCode, dataStr, extras, ordered);
+                    resultCode, dataStr, extras, ordered, sticky);
             return true;
         }
 
@@ -716,7 +717,7 @@
     }
     
     public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
-            int resultCode, String dataStr, Bundle extras, boolean ordered)
+            int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -726,6 +727,7 @@
         data.writeString(dataStr);
         data.writeBundle(extras);
         data.writeInt(ordered ? 1 : 0);
+        data.writeInt(sticky ? 1 : 0);
         mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8dda898..89a52fd 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -91,7 +91,7 @@
     void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
             throws RemoteException;
     void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
-            int resultCode, String data, Bundle extras, boolean ordered)
+            int resultCode, String data, Bundle extras, boolean ordered, boolean sticky)
             throws RemoteException;
     void scheduleLowMemory() throws RemoteException;
     void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 18d9b92..be1dc4a 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -147,7 +147,7 @@
             mHandler = handler;
         }
         public void performReceive(Intent intent, int resultCode,
-                String data, Bundle extras, boolean serialized) {
+                String data, Bundle extras, boolean serialized, boolean sticky) {
             mIntent = intent;
             mResultCode = resultCode;
             mResultData = data;
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index b391c57d..b63d026 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -384,6 +384,24 @@
     }
     
     /**
+     * Returns true if the receiver is currently processing an ordered
+     * broadcast.
+     */
+    public final boolean isOrderedBroadcast() {
+        return mOrderedHint;
+    }
+    
+    /**
+     * Returns true if the receiver is currently processing the initial
+     * value of a sticky broadcast -- that is, the value that was last
+     * broadcast and is currently held in the sticky cache, so this is
+     * not directly the result of a broadcast right now.
+     */
+    public final boolean isInitialStickyBroadcast() {
+        return mInitialStickyHint;
+    }
+    
+    /**
      * For internal use, sets the hint about whether this BroadcastReceiver is
      * running in ordered mode.
      */
@@ -392,6 +410,14 @@
     }
     
     /**
+     * For internal use, sets the hint about whether this BroadcastReceiver is
+     * receiving the initial sticky broadcast value. @hide
+     */
+    public final void setInitialStickyHint(boolean isInitialSticky) {
+        mInitialStickyHint = isInitialSticky;
+    }
+    
+    /**
      * Control inclusion of debugging help for mismatched
      * calls to {@ Context#registerReceiver(BroadcastReceiver, IntentFilter)
      * Context.registerReceiver()}.
@@ -414,7 +440,10 @@
     }
     
     void checkSynchronousHint() {
-        if (mOrderedHint) {
+        // Note that we don't assert when receiving the initial sticky value,
+        // since that may have come from an ordered broadcast.  We'll catch
+        // them later when the real broadcast happens again.
+        if (mOrderedHint || mInitialStickyHint) {
             return;
         }
         RuntimeException e = new RuntimeException(
@@ -429,5 +458,6 @@
     private boolean mAbortBroadcast;
     private boolean mDebugUnregister;
     private boolean mOrderedHint;
+    private boolean mInitialStickyHint;
 }
 
diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl
index 443db2d..6f2f7c4 100755
--- a/core/java/android/content/IIntentReceiver.aidl
+++ b/core/java/android/content/IIntentReceiver.aidl
@@ -28,6 +28,6 @@
  */
 oneway interface IIntentReceiver {
     void performReceive(in Intent intent, int resultCode,
-                        String data, in Bundle extras, boolean ordered);
+            String data, in Bundle extras, boolean ordered, boolean sticky);
 }
 
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index c8f7aa9..e182021 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -113,7 +113,7 @@
             mHandler = handler;
         }
         public void performReceive(Intent intent, int resultCode,
-                String data, Bundle extras, boolean serialized) {
+                String data, Bundle extras, boolean serialized, boolean sticky) {
             mIntent = intent;
             mResultCode = resultCode;
             mResultData = data;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index cd39d0d..34302b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8389,7 +8389,8 @@
                         if (i == 0) {
                             finisher = new IIntentReceiver.Stub() {
                                 public void performReceive(Intent intent, int resultCode,
-                                        String data, Bundle extras, boolean ordered)
+                                        String data, Bundle extras, boolean ordered,
+                                        boolean sticky)
                                         throws RemoteException {
                                     synchronized (ActivityManagerService.this) {
                                         mDidUpdate = true;
@@ -11571,7 +11572,7 @@
                     Intent intent = (Intent)allSticky.get(i);
                     BroadcastRecord r = new BroadcastRecord(intent, null,
                             null, -1, -1, null, receivers, null, 0, null, null,
-                            false);
+                            false, true);
                     if (mParallelBroadcasts.size() == 0) {
                         scheduleBroadcastsLocked();
                     }
@@ -11796,7 +11797,7 @@
             BroadcastRecord r = new BroadcastRecord(intent, callerApp,
                     callerPackage, callingPid, callingUid, requiredPermission,
                     registeredReceivers, resultTo, resultCode, resultData, map,
-                    ordered);
+                    ordered, false);
             if (DEBUG_BROADCAST) Log.v(
                     TAG, "Enqueueing parallel broadcast " + r
                     + ": prev had " + mParallelBroadcasts.size());
@@ -11875,7 +11876,7 @@
                 || resultTo != null) {
             BroadcastRecord r = new BroadcastRecord(intent, callerApp,
                     callerPackage, callingPid, callingUid, requiredPermission,
-                    receivers, resultTo, resultCode, resultData, map, ordered);
+                    receivers, resultTo, resultCode, resultData, map, ordered, false);
             if (DEBUG_BROADCAST) Log.v(
                     TAG, "Enqueueing ordered broadcast " + r
                     + ": prev had " + mOrderedBroadcasts.size());
@@ -12179,15 +12180,15 @@
     }
 
     static void performReceive(ProcessRecord app, IIntentReceiver receiver,
-            Intent intent, int resultCode, String data,
-            Bundle extras, boolean ordered) throws RemoteException {
+            Intent intent, int resultCode, String data, Bundle extras,
+            boolean ordered, boolean sticky) throws RemoteException {
         if (app != null && app.thread != null) {
             // If we have an app thread, do the call through that so it is
             // correctly ordered with other one-way calls.
             app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
-                    data, extras, ordered);
+                    data, extras, ordered, sticky);
         } else {
-            receiver.performReceive(intent, resultCode, data, extras, ordered);
+            receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
         }
     }
     
@@ -12251,7 +12252,7 @@
                 }
                 performReceive(filter.receiverList.app, filter.receiverList.receiver,
                     new Intent(r.intent), r.resultCode,
-                    r.resultData, r.resultExtras, r.ordered);
+                    r.resultData, r.resultExtras, r.ordered, r.sticky);
                 if (ordered) {
                     r.state = BroadcastRecord.CALL_DONE_RECEIVE;
                 }
@@ -12384,7 +12385,7 @@
                             }
                             performReceive(r.callerApp, r.resultTo,
                                 new Intent(r.intent), r.resultCode,
-                                r.resultData, r.resultExtras, false);
+                                r.resultData, r.resultExtras, false, false);
                         } catch (RemoteException e) {
                             Log.w(TAG, "Failure sending broadcast result of " + r.intent, e);
                         }
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index da55049..db0a6cb 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -39,7 +39,9 @@
     final String callerPackage; // who sent this
     final int callingPid;   // the pid of who sent this
     final int callingUid;   // the uid of who sent this
-    String requiredPermission; // a permission the caller has required
+    final boolean ordered;  // serialize the send to receivers?
+    final boolean sticky;   // originated from existing sticky data?
+    final String requiredPermission; // a permission the caller has required
     final List receivers;   // contains BroadcastFilter and ResolveInfo
     final IIntentReceiver resultTo; // who receives final result if non-null
     long dispatchTime;      // when dispatch started on this set of receivers
@@ -48,7 +50,6 @@
     String resultData;      // current result data value.
     Bundle resultExtras;    // current result extra data values.
     boolean resultAbort;    // current result abortBroadcast value.
-    boolean ordered;        // serialize the send to receivers?
     int nextReceiver;       // next receiver to be executed.
     IBinder receiver;       // who is currently running, null if none.
     int state;
@@ -86,7 +87,7 @@
               + " resultCode=" + resultCode + " resultData=" + resultData);
         pw.println(prefix + "resultExtras=" + resultExtras);
         pw.println(prefix + "resultAbort=" + resultAbort
-                + " ordered=" + ordered);
+                + " ordered=" + ordered + " sticky=" + sticky);
         pw.println(prefix + "nextReceiver=" + nextReceiver
               + " receiver=" + receiver);
         pw.println(prefix + "curFilter=" + curFilter);
@@ -122,7 +123,8 @@
     BroadcastRecord(Intent _intent, ProcessRecord _callerApp, String _callerPackage,
             int _callingPid, int _callingUid, String _requiredPermission,
             List _receivers, IIntentReceiver _resultTo, int _resultCode,
-            String _resultData, Bundle _resultExtras, boolean _serialized) {
+            String _resultData, Bundle _resultExtras, boolean _serialized,
+            boolean _sticky) {
         intent = _intent;
         callerApp = _callerApp;
         callerPackage = _callerPackage;
@@ -135,6 +137,7 @@
         resultData = _resultData;
         resultExtras = _resultExtras;
         ordered = _serialized;
+        sticky = _sticky;
         nextReceiver = 0;
         state = IDLE;
     }
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index a753d05..b3086d5 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -248,7 +248,7 @@
                 if (sendFinish) {
                     try {
                         finishedReceiver.performReceive(new Intent(finalIntent), 0,
-                                null, null, false);
+                                null, null, false, false);
                     } catch (RemoteException e) {
                     }
                 }