Move BroadcastQueue out of the ActivityManager class.

Change-Id: Ib468481588a1aa506ff00f3c4b1a6ecf672c7b99
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4e1e9d9..ad8fea9 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -291,813 +291,12 @@
     final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
             = new ArrayList<PendingActivityLaunch>();
     
-    /**
-     * BROADCASTS
-     *
-     * We keep two broadcast queues and associated bookkeeping, one for those at
-     * foreground priority, and one for normal (background-priority) broadcasts.
-     */
-    public class BroadcastQueue {
-        static final String TAG = "BroadcastQueue";
 
-        static final int MAX_BROADCAST_HISTORY = 25;
-
-        /**
-         * Recognizable moniker for this queue
-         */
-        String mQueueName;
-
-        /**
-         * Timeout period for this queue's broadcasts
-         */
-        long mTimeoutPeriod;
-
-        /**
-         * Lists of all active broadcasts that are to be executed immediately
-         * (without waiting for another broadcast to finish).  Currently this only
-         * contains broadcasts to registered receivers, to avoid spinning up
-         * a bunch of processes to execute IntentReceiver components.  Background-
-         * and foreground-priority broadcasts are queued separately.
-         */
-        final ArrayList<BroadcastRecord> mParallelBroadcasts
-                = new ArrayList<BroadcastRecord>();
-        /**
-         * List of all active broadcasts that are to be executed one at a time.
-         * The object at the top of the list is the currently activity broadcasts;
-         * those after it are waiting for the top to finish.  As with parallel
-         * broadcasts, separate background- and foreground-priority queues are
-         * maintained.
-         */
-        final ArrayList<BroadcastRecord> mOrderedBroadcasts
-                = new ArrayList<BroadcastRecord>();
-
-        /**
-         * Historical data of past broadcasts, for debugging.
-         */
-        final BroadcastRecord[] mBroadcastHistory
-                = new BroadcastRecord[MAX_BROADCAST_HISTORY];
-
-        /**
-         * Set when we current have a BROADCAST_INTENT_MSG in flight.
-         */
-        boolean mBroadcastsScheduled = false;
-
-        /**
-         * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
-         */
-        boolean mPendingBroadcastTimeoutMessage;
-
-        /**
-         * Intent broadcasts that we have tried to start, but are
-         * waiting for the application's process to be created.  We only
-         * need one per scheduling class (instead of a list) because we always
-         * process broadcasts one at a time, so no others can be started while
-         * waiting for this one.
-         */
-        BroadcastRecord mPendingBroadcast = null;
-
-        /**
-         * The receiver index that is pending, to restart the broadcast if needed.
-         */
-        int mPendingBroadcastRecvIndex;
-
-        BroadcastQueue(String name, long timeoutPeriod) {
-            mQueueName = name;
-            mTimeoutPeriod = timeoutPeriod;
-        }
-
-        public boolean isPendingBroadcastProcessLocked(int pid) {
-            return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
-        }
-
-        public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
-            mParallelBroadcasts.add(r);
-        }
-
-        public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
-            mOrderedBroadcasts.add(r);
-        }
-
-        public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
-            for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
-                if (r.intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "***** DROPPING PARALLEL ["
-                    + mQueueName + "]: " + r.intent);
-                    mParallelBroadcasts.set(i, r);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) {
-            for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
-                if (r.intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "***** DROPPING ORDERED ["
-                            + mQueueName + "]: " + r.intent);
-                    mOrderedBroadcasts.set(i, r);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
-            boolean didSomething = false;
-            final BroadcastRecord br = mPendingBroadcast;
-            if (br != null && br.curApp.pid == app.pid) {
-                try {
-                    mPendingBroadcast = null;
-                    processCurBroadcastLocked(br, app);
-                    didSomething = true;
-                } catch (Exception e) {
-                    Slog.w(TAG, "Exception in new application when starting receiver "
-                            + br.curComponent.flattenToShortString(), e);
-                    logBroadcastReceiverDiscardLocked(br);
-                    finishReceiverLocked(br, br.resultCode, br.resultData,
-                            br.resultExtras, br.resultAbort, true);
-                    scheduleBroadcastsLocked();
-                    // We need to reset the state if we fails to start the receiver.
-                    br.state = BroadcastRecord.IDLE;
-                    throw new RuntimeException(e.getMessage());
-                }
-            }
-            return didSomething;
-        }
-
-        public void skipPendingBroadcastLocked(int pid) {
-            final BroadcastRecord br = mPendingBroadcast;
-            if (br != null && br.curApp.pid == pid) {
-                br.state = BroadcastRecord.IDLE;
-                br.nextReceiver = mPendingBroadcastRecvIndex;
-                mPendingBroadcast = null;
-                scheduleBroadcastsLocked();
-            }
-        }
-
-        public void skipCurrentReceiverLocked(ProcessRecord app) {
-            boolean reschedule = false;
-            BroadcastRecord r = app.curReceiver;
-            if (r != null) {
-                // The current broadcast is waiting for this app's receiver
-                // to be finished.  Looks like that's not going to happen, so
-                // let the broadcast continue.
-                logBroadcastReceiverDiscardLocked(r);
-                finishReceiverLocked(r, r.resultCode, r.resultData,
-                        r.resultExtras, r.resultAbort, true);
-                reschedule = true;
-            }
-
-            r = mPendingBroadcast;
-            if (r != null && r.curApp == app) {
-                if (DEBUG_BROADCAST) Slog.v(TAG,
-                        "[" + mQueueName + "] skip & discard pending app " + r);
-                logBroadcastReceiverDiscardLocked(r);
-                finishReceiverLocked(r, r.resultCode, r.resultData,
-                        r.resultExtras, r.resultAbort, true);
-                reschedule = true;
-            }
-            if (reschedule) {
-                scheduleBroadcastsLocked();
-            }
-        }
-
-        public void scheduleBroadcastsLocked() {
-            if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
-                    + mQueueName + "]: current="
-                    + mBroadcastsScheduled);
-
-            if (mBroadcastsScheduled) {
-                return;
-            }
-            mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
-            mBroadcastsScheduled = true;
-        }
-
-        public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
-            if (mOrderedBroadcasts.size() > 0) {
-                final BroadcastRecord r = mOrderedBroadcasts.get(0);
-                if (r != null && r.receiver == receiver) {
-                    return r;
-                }
-            }
-            return null;
-        }
-
-        public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
-                String resultData, Bundle resultExtras, boolean resultAbort,
-                boolean explicit) {
-            int state = r.state;
-            r.state = BroadcastRecord.IDLE;
-            if (state == BroadcastRecord.IDLE) {
-                if (explicit) {
-                    Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
-                }
-            }
-            r.receiver = null;
-            r.intent.setComponent(null);
-            if (r.curApp != null) {
-                r.curApp.curReceiver = null;
-            }
-            if (r.curFilter != null) {
-                r.curFilter.receiverList.curBroadcast = null;
-            }
-            r.curFilter = null;
-            r.curApp = null;
-            r.curComponent = null;
-            r.curReceiver = null;
-            mPendingBroadcast = null;
-
-            r.resultCode = resultCode;
-            r.resultData = resultData;
-            r.resultExtras = resultExtras;
-            r.resultAbort = resultAbort;
-
-            // We will process the next receiver right now if this is finishing
-            // an app receiver (which is always asynchronous) or after we have
-            // come back from calling a receiver.
-            return state == BroadcastRecord.APP_RECEIVE
-                    || state == BroadcastRecord.CALL_DONE_RECEIVE;
-        }
-
-        private final void processNextBroadcast(boolean fromMsg) {
-            synchronized(ActivityManagerService.this) {
-                BroadcastRecord r;
-
-                if (DEBUG_BROADCAST) Slog.v(TAG, "processNextBroadcast ["
-                        + mQueueName + "]: "
-                        + mParallelBroadcasts.size() + " broadcasts, "
-                        + mOrderedBroadcasts.size() + " ordered broadcasts");
-
-                updateCpuStats();
-
-                if (fromMsg) {
-                    mBroadcastsScheduled = false;
-                }
-
-                // First, deliver any non-serialized broadcasts right away.
-                while (mParallelBroadcasts.size() > 0) {
-                    r = mParallelBroadcasts.remove(0);
-                    r.dispatchTime = SystemClock.uptimeMillis();
-                    r.dispatchClockTime = System.currentTimeMillis();
-                    final int N = r.receivers.size();
-                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
-                            + mQueueName + "] " + r);
-                    for (int i=0; i<N; i++) {
-                        Object target = r.receivers.get(i);
-                        if (DEBUG_BROADCAST)  Slog.v(TAG,
-                                "Delivering non-ordered on [" + mQueueName + "] to registered "
-                                + target + ": " + r);
-                        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
-                    }
-                    addBroadcastToHistoryLocked(r);
-                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
-                            + mQueueName + "] " + r);
-                }
-
-                // Now take care of the next serialized one...
-
-                // If we are waiting for a process to come up to handle the next
-                // broadcast, then do nothing at this point.  Just in case, we
-                // check that the process we're waiting for still exists.
-                if (mPendingBroadcast != null) {
-                    if (DEBUG_BROADCAST_LIGHT) {
-                        Slog.v(TAG, "processNextBroadcast ["
-                                + mQueueName + "]: waiting for "
-                                + mPendingBroadcast.curApp);
-                    }
-
-                    boolean isDead;
-                    synchronized (mPidsSelfLocked) {
-                        isDead = (mPidsSelfLocked.get(mPendingBroadcast.curApp.pid) == null);
-                    }
-                    if (!isDead) {
-                        // It's still alive, so keep waiting
-                        return;
-                    } else {
-                        Slog.w(TAG, "pending app  ["
-                                + mQueueName + "]" + mPendingBroadcast.curApp
-                                + " died before responding to broadcast");
-                        mPendingBroadcast.state = BroadcastRecord.IDLE;
-                        mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
-                        mPendingBroadcast = null;
-                    }
-                }
-
-                boolean looped = false;
-                
-                do {
-                    if (mOrderedBroadcasts.size() == 0) {
-                        // No more broadcasts pending, so all done!
-                        scheduleAppGcsLocked();
-                        if (looped) {
-                            // If we had finished the last ordered broadcast, then
-                            // make sure all processes have correct oom and sched
-                            // adjustments.
-                            updateOomAdjLocked();
-                        }
-                        return;
-                    }
-                    r = mOrderedBroadcasts.get(0);
-                    boolean forceReceive = false;
-
-                    // Ensure that even if something goes awry with the timeout
-                    // detection, we catch "hung" broadcasts here, discard them,
-                    // and continue to make progress.
-                    //
-                    // This is only done if the system is ready so that PRE_BOOT_COMPLETED
-                    // receivers don't get executed with timeouts. They're intended for
-                    // one time heavy lifting after system upgrades and can take
-                    // significant amounts of time.
-                    int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
-                    if (mProcessesReady && r.dispatchTime > 0) {
-                        long now = SystemClock.uptimeMillis();
-                        if ((numReceivers > 0) &&
-                                (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
-                            Slog.w(TAG, "Hung broadcast ["
-                                    + mQueueName + "] discarded after timeout failure:"
-                                    + " now=" + now
-                                    + " dispatchTime=" + r.dispatchTime
-                                    + " startTime=" + r.receiverTime
-                                    + " intent=" + r.intent
-                                    + " numReceivers=" + numReceivers
-                                    + " nextReceiver=" + r.nextReceiver
-                                    + " state=" + r.state);
-                            broadcastTimeoutLocked(false); // forcibly finish this broadcast
-                            forceReceive = true;
-                            r.state = BroadcastRecord.IDLE;
-                        }
-                    }
-
-                    if (r.state != BroadcastRecord.IDLE) {
-                        if (DEBUG_BROADCAST) Slog.d(TAG,
-                                "processNextBroadcast("
-                                + mQueueName + ") called when not idle (state="
-                                + r.state + ")");
-                        return;
-                    }
-
-                    if (r.receivers == null || r.nextReceiver >= numReceivers
-                            || r.resultAbort || forceReceive) {
-                        // No more receivers for this broadcast!  Send the final
-                        // result if requested...
-                        if (r.resultTo != null) {
-                            try {
-                                if (DEBUG_BROADCAST) {
-                                    int seq = r.intent.getIntExtra("seq", -1);
-                                    Slog.i(TAG, "Finishing broadcast ["
-                                            + mQueueName + "] " + r.intent.getAction()
-                                            + " seq=" + seq + " app=" + r.callerApp);
-                                }
-                                performReceiveLocked(r.callerApp, r.resultTo,
-                                    new Intent(r.intent), r.resultCode,
-                                    r.resultData, r.resultExtras, false, false);
-                                // Set this to null so that the reference
-                                // (local and remote) isnt kept in the mBroadcastHistory.
-                                r.resultTo = null;
-                            } catch (RemoteException e) {
-                                Slog.w(TAG, "Failure ["
-                                        + mQueueName + "] sending broadcast result of "
-                                        + r.intent, e);
-                            }
-                        }
-
-                        if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
-                        cancelBroadcastTimeoutLocked();
-
-                        if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
-                                + r);
-
-                        // ... and on to the next...
-                        addBroadcastToHistoryLocked(r);
-                        mOrderedBroadcasts.remove(0);
-                        r = null;
-                        looped = true;
-                        continue;
-                    }
-                } while (r == null);
-
-                // Get the next receiver...
-                int recIdx = r.nextReceiver++;
-
-                // Keep track of when this receiver started, and make sure there
-                // is a timeout message pending to kill it if need be.
-                r.receiverTime = SystemClock.uptimeMillis();
-                if (recIdx == 0) {
-                    r.dispatchTime = r.receiverTime;
-                    r.dispatchClockTime = System.currentTimeMillis();
-                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast ["
-                            + mQueueName + "] " + r);
-                }
-                if (! mPendingBroadcastTimeoutMessage) {
-                    long timeoutTime = r.receiverTime + mTimeoutPeriod;
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "Submitting BROADCAST_TIMEOUT_MSG ["
-                            + mQueueName + "] for " + r + " at " + timeoutTime);
-                    setBroadcastTimeoutLocked(timeoutTime);
-                }
-
-                Object nextReceiver = r.receivers.get(recIdx);
-                if (nextReceiver instanceof BroadcastFilter) {
-                    // Simple case: this is a registered receiver who gets
-                    // a direct call.
-                    BroadcastFilter filter = (BroadcastFilter)nextReceiver;
-                    if (DEBUG_BROADCAST)  Slog.v(TAG,
-                            "Delivering ordered ["
-                            + mQueueName + "] to registered "
-                            + filter + ": " + r);
-                    deliverToRegisteredReceiverLocked(r, filter, r.ordered);
-                    if (r.receiver == null || !r.ordered) {
-                        // The receiver has already finished, so schedule to
-                        // process the next one.
-                        if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing ["
-                                + mQueueName + "]: ordered="
-                                + r.ordered + " receiver=" + r.receiver);
-                        r.state = BroadcastRecord.IDLE;
-                        scheduleBroadcastsLocked();
-                    }
-                    return;
-                }
-
-                // Hard case: need to instantiate the receiver, possibly
-                // starting its application process to host it.
-
-                ResolveInfo info =
-                    (ResolveInfo)nextReceiver;
-
-                boolean skip = false;
-                int perm = checkComponentPermission(info.activityInfo.permission,
-                        r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
-                        info.activityInfo.exported);
-                if (perm != PackageManager.PERMISSION_GRANTED) {
-                    if (!info.activityInfo.exported) {
-                        Slog.w(TAG, "Permission Denial: broadcasting "
-                                + r.intent.toString()
-                                + " from " + r.callerPackage + " (pid=" + r.callingPid
-                                + ", uid=" + r.callingUid + ")"
-                                + " is not exported from uid " + info.activityInfo.applicationInfo.uid
-                                + " due to receiver " + info.activityInfo.packageName
-                                + "/" + info.activityInfo.name);
-                    } else {
-                        Slog.w(TAG, "Permission Denial: broadcasting "
-                                + r.intent.toString()
-                                + " from " + r.callerPackage + " (pid=" + r.callingPid
-                                + ", uid=" + r.callingUid + ")"
-                                + " requires " + info.activityInfo.permission
-                                + " due to receiver " + info.activityInfo.packageName
-                                + "/" + info.activityInfo.name);
-                    }
-                    skip = true;
-                }
-                if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
-                    r.requiredPermission != null) {
-                    try {
-                        perm = AppGlobals.getPackageManager().
-                                checkPermission(r.requiredPermission,
-                                        info.activityInfo.applicationInfo.packageName);
-                    } catch (RemoteException e) {
-                        perm = PackageManager.PERMISSION_DENIED;
-                    }
-                    if (perm != PackageManager.PERMISSION_GRANTED) {
-                        Slog.w(TAG, "Permission Denial: receiving "
-                                + r.intent + " to "
-                                + info.activityInfo.applicationInfo.packageName
-                                + " requires " + r.requiredPermission
-                                + " due to sender " + r.callerPackage
-                                + " (uid " + r.callingUid + ")");
-                        skip = true;
-                    }
-                }
-                if (r.curApp != null && r.curApp.crashing) {
-                    // If the target process is crashing, just skip it.
-                    if (DEBUG_BROADCAST)  Slog.v(TAG,
-                            "Skipping deliver ordered ["
-                            + mQueueName + "] " + r + " to " + r.curApp
-                            + ": process crashing");
-                    skip = true;
-                }
-
-                if (skip) {
-                    if (DEBUG_BROADCAST)  Slog.v(TAG,
-                            "Skipping delivery of ordered ["
-                            + mQueueName + "] " + r + " for whatever reason");
-                    r.receiver = null;
-                    r.curFilter = null;
-                    r.state = BroadcastRecord.IDLE;
-                    scheduleBroadcastsLocked();
-                    return;
-                }
-
-                r.state = BroadcastRecord.APP_RECEIVE;
-                String targetProcess = info.activityInfo.processName;
-                r.curComponent = new ComponentName(
-                        info.activityInfo.applicationInfo.packageName,
-                        info.activityInfo.name);
-                if (r.callingUid != Process.SYSTEM_UID) {
-                    info.activityInfo = getActivityInfoForUser(info.activityInfo, UserId
-                            .getUserId(r.callingUid));
-                }
-                r.curReceiver = info.activityInfo;
-                if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
-                    Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
-                            + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
-                            + info.activityInfo.applicationInfo.uid);
-                }
-
-                // Broadcast is being executed, its package can't be stopped.
-                try {
-                    AppGlobals.getPackageManager().setPackageStoppedState(
-                            r.curComponent.getPackageName(), false);
-                } catch (RemoteException e) {
-                } catch (IllegalArgumentException e) {
-                    Slog.w(TAG, "Failed trying to unstop package "
-                            + r.curComponent.getPackageName() + ": " + e);
-                }
-
-                // Is this receiver's application already running?
-                ProcessRecord app = getProcessRecordLocked(targetProcess,
-                        info.activityInfo.applicationInfo.uid);
-                if (app != null && app.thread != null) {
-                    try {
-                        app.addPackage(info.activityInfo.packageName);
-                        processCurBroadcastLocked(r, app);
-                        return;
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Exception when sending broadcast to "
-                              + r.curComponent, e);
-                    }
-
-                    // If a dead object exception was thrown -- fall through to
-                    // restart the application.
-                }
-
-                // Not running -- get it started, to be executed when the app comes up.
-                if (DEBUG_BROADCAST)  Slog.v(TAG,
-                        "Need to start app ["
-                        + mQueueName + "] " + targetProcess + " for broadcast " + r);
-                if ((r.curApp=startProcessLocked(targetProcess,
-                        info.activityInfo.applicationInfo, true,
-                        r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
-                        "broadcast", r.curComponent,
-                        (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false))
-                                == null) {
-                    // Ah, this recipient is unavailable.  Finish it if necessary,
-                    // and mark the broadcast record as ready for the next.
-                    Slog.w(TAG, "Unable to launch app "
-                            + info.activityInfo.applicationInfo.packageName + "/"
-                            + info.activityInfo.applicationInfo.uid + " for broadcast "
-                            + r.intent + ": process is bad");
-                    logBroadcastReceiverDiscardLocked(r);
-                    finishReceiverLocked(r, r.resultCode, r.resultData,
-                            r.resultExtras, r.resultAbort, true);
-                    scheduleBroadcastsLocked();
-                    r.state = BroadcastRecord.IDLE;
-                    return;
-                }
-
-                mPendingBroadcast = r;
-                mPendingBroadcastRecvIndex = recIdx;
-            }
-        }
-
-        final void setBroadcastTimeoutLocked(long timeoutTime) {
-            if (! mPendingBroadcastTimeoutMessage) {
-                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
-                mHandler.sendMessageAtTime(msg, timeoutTime);
-                mPendingBroadcastTimeoutMessage = true;
-            }
-        }
-
-        final void cancelBroadcastTimeoutLocked() {
-            if (mPendingBroadcastTimeoutMessage) {
-                mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
-                mPendingBroadcastTimeoutMessage = false;
-            }
-        }
-
-        final void broadcastTimeoutLocked(boolean fromMsg) {
-            if (fromMsg) {
-                mPendingBroadcastTimeoutMessage = false;
-            }
-
-            if (mOrderedBroadcasts.size() == 0) {
-                return;
-            }
-
-            long now = SystemClock.uptimeMillis();
-            BroadcastRecord r = mOrderedBroadcasts.get(0);
-            if (fromMsg) {
-                if (mDidDexOpt) {
-                    // Delay timeouts until dexopt finishes.
-                    mDidDexOpt = false;
-                    long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
-                    setBroadcastTimeoutLocked(timeoutTime);
-                    return;
-                }
-                if (! mProcessesReady) {
-                    // Only process broadcast timeouts if the system is ready. That way
-                    // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
-                    // to do heavy lifting for system up.
-                    return;
-                }
-
-                long timeoutTime = r.receiverTime + mTimeoutPeriod;
-                if (timeoutTime > now) {
-                    // We can observe premature timeouts because we do not cancel and reset the
-                    // broadcast timeout message after each receiver finishes.  Instead, we set up
-                    // an initial timeout then kick it down the road a little further as needed
-                    // when it expires.
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "Premature timeout ["
-                            + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
-                            + timeoutTime);
-                    setBroadcastTimeoutLocked(timeoutTime);
-                    return;
-                }
-            }
-
-            Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
-                    + ", started " + (now - r.receiverTime) + "ms ago");
-            r.receiverTime = now;
-            r.anrCount++;
-
-            // Current receiver has passed its expiration date.
-            if (r.nextReceiver <= 0) {
-                Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
-                return;
-            }
-
-            ProcessRecord app = null;
-            String anrMessage = null;
-
-            Object curReceiver = r.receivers.get(r.nextReceiver-1);
-            Slog.w(TAG, "Receiver during timeout: " + curReceiver);
-            logBroadcastReceiverDiscardLocked(r);
-            if (curReceiver instanceof BroadcastFilter) {
-                BroadcastFilter bf = (BroadcastFilter)curReceiver;
-                if (bf.receiverList.pid != 0
-                        && bf.receiverList.pid != MY_PID) {
-                    synchronized (ActivityManagerService.this.mPidsSelfLocked) {
-                        app = ActivityManagerService.this.mPidsSelfLocked.get(
-                                bf.receiverList.pid);
-                    }
-                }
-            } else {
-                app = r.curApp;
-            }
-
-            if (app != null) {
-                anrMessage = "Broadcast of " + r.intent.toString();
-            }
-
-            if (mPendingBroadcast == r) {
-                mPendingBroadcast = null;
-            }
-
-            // Move on to the next receiver.
-            finishReceiverLocked(r, r.resultCode, r.resultData,
-                    r.resultExtras, r.resultAbort, true);
-            scheduleBroadcastsLocked();
-
-            if (anrMessage != null) {
-                // Post the ANR to the handler since we do not want to process ANRs while
-                // potentially holding our lock.
-                mHandler.post(new AppNotResponding(app, anrMessage));
-            }
-        }
-
-        private final void addBroadcastToHistoryLocked(BroadcastRecord r) {
-            if (r.callingUid < 0) {
-                // This was from a registerReceiver() call; ignore it.
-                return;
-            }
-            System.arraycopy(mBroadcastHistory, 0, mBroadcastHistory, 1,
-                    MAX_BROADCAST_HISTORY-1);
-            r.finishTime = SystemClock.uptimeMillis();
-            mBroadcastHistory[0] = r;
-        }
-
-        final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
-            if (r.nextReceiver > 0) {
-                Object curReceiver = r.receivers.get(r.nextReceiver-1);
-                if (curReceiver instanceof BroadcastFilter) {
-                    BroadcastFilter bf = (BroadcastFilter) curReceiver;
-                    EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
-                            System.identityHashCode(r),
-                            r.intent.getAction(),
-                            r.nextReceiver - 1,
-                            System.identityHashCode(bf));
-                } else {
-                    EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-                            System.identityHashCode(r),
-                            r.intent.getAction(),
-                            r.nextReceiver - 1,
-                            ((ResolveInfo)curReceiver).toString());
-                }
-            } else {
-                Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
-                        + r);
-                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-                        System.identityHashCode(r),
-                        r.intent.getAction(),
-                        r.nextReceiver,
-                        "NONE");
-            }
-        }
-
-        final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-                int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
-            if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
-                    || mPendingBroadcast != null) {
-                boolean printed = false;
-                for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
-                    BroadcastRecord br = mParallelBroadcasts.get(i);
-                    if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) {
-                            pw.println();
-                            needSep = false;
-                        }
-                        printed = true;
-                        pw.println("  Active broadcasts [" + mQueueName + "]:");
-                    }
-                    pw.println("  Broadcast #" + i + ":");
-                    br.dump(pw, "    ");
-                }
-                printed = false;
-                needSep = true;
-                for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
-                    BroadcastRecord br = mOrderedBroadcasts.get(i);
-                    if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) {
-                            pw.println();
-                        }
-                        needSep = true;
-                        pw.println("  Active ordered broadcasts [" + mQueueName + "]:");
-                    }
-                    pw.println("  Ordered Broadcast #" + i + ":");
-                    mOrderedBroadcasts.get(i).dump(pw, "    ");
-                }
-                if (dumpPackage == null || (mPendingBroadcast != null
-                        && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
-                    if (needSep) {
-                        pw.println();
-                    }
-                    pw.println("  Pending broadcast [" + mQueueName + "]:");
-                    if (mPendingBroadcast != null) {
-                        mPendingBroadcast.dump(pw, "    ");
-                    } else {
-                        pw.println("    (null)");
-                    }
-                    needSep = true;
-                }
-            }
-
-            boolean printed = false;
-            for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
-                BroadcastRecord r = mBroadcastHistory[i];
-                if (r == null) {
-                    break;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
-                    continue;
-                }
-                if (!printed) {
-                    if (needSep) {
-                        pw.println();
-                    }
-                    needSep = true;
-                    pw.println("  Historical broadcasts [" + mQueueName + "]:");
-                    printed = true;
-                }
-                if (dumpAll) {
-                    pw.print("  Historical Broadcast #"); pw.print(i); pw.println(":");
-                    r.dump(pw, "    ");
-                } else {
-                    if (i >= 50) {
-                        pw.println("  ...");
-                        break;
-                    }
-                    pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
-                }
-            }
-
-            return needSep;
-        }
-    }
-
-    final BroadcastQueue mFgBroadcastQueue = new BroadcastQueue("foreground", BROADCAST_FG_TIMEOUT);
-    final BroadcastQueue mBgBroadcastQueue = new BroadcastQueue("background", BROADCAST_BG_TIMEOUT);
+    BroadcastQueue mFgBroadcastQueue;
+    BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
     // so that dispatch of foreground broadcasts gets precedence.
-    final BroadcastQueue[] mBroadcastQueues = { mFgBroadcastQueue, mBgBroadcastQueue };
+    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];
 
     BroadcastQueue broadcastQueueForIntent(Intent intent) {
         final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
@@ -1642,8 +841,6 @@
     static final int UPDATE_CONFIGURATION_MSG = 4;
     static final int GC_BACKGROUND_PROCESSES_MSG = 5;
     static final int WAIT_FOR_DEBUGGER_MSG = 6;
-    static final int BROADCAST_INTENT_MSG = 7;
-    static final int BROADCAST_TIMEOUT_MSG = 8;
     static final int SERVICE_TIMEOUT_MSG = 12;
     static final int UPDATE_TIME_ZONE = 13;
     static final int SHOW_UID_ERROR_MSG = 14;
@@ -1663,6 +860,10 @@
     static final int DISPATCH_PROCESS_DIED = 32;
     static final int REPORT_MEM_USAGE = 33;
 
+    static final int FIRST_ACTIVITY_STACK_MSG = 100;
+    static final int FIRST_BROADCAST_QUEUE_MSG = 200;
+    static final int FIRST_COMPAT_MODE_MSG = 300;
+
     AlertDialog mUidAlert;
     CompatModeDialog mCompatModeDialog;
     long mLastMemUsageReportTime = 0;
@@ -1782,18 +983,6 @@
                     }
                 }
             } break;
-            case BROADCAST_INTENT_MSG: {
-                if (DEBUG_BROADCAST) Slog.v(
-                        TAG, "Received BROADCAST_INTENT_MSG");
-                BroadcastQueue queue = (BroadcastQueue) msg.obj;
-                queue.processNextBroadcast(true);
-            } break;
-            case BROADCAST_TIMEOUT_MSG: {
-                final BroadcastQueue queue = (BroadcastQueue) msg.obj;
-                synchronized (ActivityManagerService.this) {
-                    queue.broadcastTimeoutLocked(true);
-                }
-            } break;
             case SERVICE_TIMEOUT_MSG: {
                 if (mDidDexOpt) {
                     mDidDexOpt = false;
@@ -2307,6 +1496,11 @@
     private ActivityManagerService() {
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
         
+        mFgBroadcastQueue = new BroadcastQueue(this, "foreground", BROADCAST_FG_TIMEOUT);
+        mBgBroadcastQueue = new BroadcastQueue(this, "background", BROADCAST_BG_TIMEOUT);
+        mBroadcastQueues[0] = mFgBroadcastQueue;
+        mBroadcastQueues[1] = mBgBroadcastQueue;
+
         File dataDir = Environment.getDataDirectory();
         File systemDir = new File(dataDir, "system");
         systemDir.mkdirs();
@@ -3852,21 +3046,6 @@
         }
     }
 
-    private final class AppNotResponding implements Runnable {
-        private final ProcessRecord mApp;
-        private final String mAnnotation;
-
-        public AppNotResponding(ProcessRecord app, String annotation) {
-            mApp = app;
-            mAnnotation = annotation;
-        }
-
-        @Override
-        public void run() {
-            appNotResponding(mApp, null, null, mAnnotation);
-        }
-    }
-
     final void appNotResponding(ProcessRecord app, ActivityRecord activity,
             ActivityRecord parent, final String annotation) {
         ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
@@ -13493,138 +12672,7 @@
             Binder.restoreCallingIdentity(origId);
         }
     }
-
-    private final void processCurBroadcastLocked(BroadcastRecord r,
-            ProcessRecord app) throws RemoteException {
-        if (DEBUG_BROADCAST)  Slog.v(TAG,
-                "Process cur broadcast " + r + " for app " + app);
-        if (app.thread == null) {
-            throw new RemoteException();
-        }
-        r.receiver = app.thread.asBinder();
-        r.curApp = app;
-        app.curReceiver = r;
-        updateLruProcessLocked(app, true, true);
-
-        // Tell the application to launch this receiver.
-        r.intent.setComponent(r.curComponent);
-
-        boolean started = false;
-        try {
-            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG,
-                    "Delivering to component " + r.curComponent
-                    + ": " + r);
-            ensurePackageDexOpt(r.intent.getComponent().getPackageName());
-            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
-                    compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
-                    r.resultCode, r.resultData, r.resultExtras, r.ordered);
-            if (DEBUG_BROADCAST)  Slog.v(TAG,
-                    "Process cur broadcast " + r + " DELIVERED for app " + app);
-            started = true;
-        } finally {
-            if (!started) {
-                if (DEBUG_BROADCAST)  Slog.v(TAG,
-                        "Process cur broadcast " + r + ": NOT STARTED!");
-                r.receiver = null;
-                r.curApp = null;
-                app.curReceiver = null;
-            }
-        }
-
-    }
-
-    static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
-            Intent intent, int resultCode, String data, Bundle extras,
-            boolean ordered, boolean sticky) throws RemoteException {
-        // Send the intent to the receiver asynchronously using one-way binder calls.
-        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, sticky);
-        } else {
-            receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
-        }
-    }
     
-    private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
-            BroadcastFilter filter, boolean ordered) {
-        boolean skip = false;
-        if (filter.requiredPermission != null) {
-            int perm = checkComponentPermission(filter.requiredPermission,
-                    r.callingPid, r.callingUid, -1, true);
-            if (perm != PackageManager.PERMISSION_GRANTED) {
-                Slog.w(TAG, "Permission Denial: broadcasting "
-                        + r.intent.toString()
-                        + " from " + r.callerPackage + " (pid="
-                        + r.callingPid + ", uid=" + r.callingUid + ")"
-                        + " requires " + filter.requiredPermission
-                        + " due to registered receiver " + filter);
-                skip = true;
-            }
-        }
-        if (r.requiredPermission != null) {
-            int perm = checkComponentPermission(r.requiredPermission,
-                    filter.receiverList.pid, filter.receiverList.uid, -1, true);
-            if (perm != PackageManager.PERMISSION_GRANTED) {
-                Slog.w(TAG, "Permission Denial: receiving "
-                        + r.intent.toString()
-                        + " to " + filter.receiverList.app
-                        + " (pid=" + filter.receiverList.pid
-                        + ", uid=" + filter.receiverList.uid + ")"
-                        + " requires " + r.requiredPermission
-                        + " due to sender " + r.callerPackage
-                        + " (uid " + r.callingUid + ")");
-                skip = true;
-            }
-        }
-
-        if (!skip) {
-            // If this is not being sent as an ordered broadcast, then we
-            // don't want to touch the fields that keep track of the current
-            // state of ordered broadcasts.
-            if (ordered) {
-                r.receiver = filter.receiverList.receiver.asBinder();
-                r.curFilter = filter;
-                filter.receiverList.curBroadcast = r;
-                r.state = BroadcastRecord.CALL_IN_RECEIVE;
-                if (filter.receiverList.app != null) {
-                    // Bump hosting application to no longer be in background
-                    // scheduling class.  Note that we can't do that if there
-                    // isn't an app...  but we can only be in that case for
-                    // things that directly call the IActivityManager API, which
-                    // are already core system stuff so don't matter for this.
-                    r.curApp = filter.receiverList.app;
-                    filter.receiverList.app.curReceiver = r;
-                    updateOomAdjLocked();
-                }
-            }
-            try {
-                if (DEBUG_BROADCAST_LIGHT) {
-                    int seq = r.intent.getIntExtra("seq", -1);
-                    Slog.i(TAG, "Delivering to " + filter
-                            + " (seq=" + seq + "): " + r);
-                }
-                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
-                    new Intent(r.intent), r.resultCode,
-                    r.resultData, r.resultExtras, r.ordered, r.initialSticky);
-                if (ordered) {
-                    r.state = BroadcastRecord.CALL_DONE_RECEIVE;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
-                if (ordered) {
-                    r.receiver = null;
-                    r.curFilter = null;
-                    filter.receiverList.curBroadcast = null;
-                    if (filter.receiverList.app != null) {
-                        filter.receiverList.app.curReceiver = null;
-                    }
-                }
-            }
-        }
-    }
-
     // =========================================================
     // INSTRUMENTATION
     // =========================================================
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index f59f0c1..e8c8275 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -283,13 +283,13 @@
 
     private int mCurrentUser;
 
-    static final int SLEEP_TIMEOUT_MSG = 8;
-    static final int PAUSE_TIMEOUT_MSG = 9;
-    static final int IDLE_TIMEOUT_MSG = 10;
-    static final int IDLE_NOW_MSG = 11;
-    static final int LAUNCH_TIMEOUT_MSG = 16;
-    static final int DESTROY_TIMEOUT_MSG = 17;
-    static final int RESUME_TOP_ACTIVITY_MSG = 19;
+    static final int SLEEP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG;
+    static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
+    static final int IDLE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
+    static final int IDLE_NOW_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
+    static final int LAUNCH_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
+    static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+    static final int RESUME_TOP_ACTIVITY_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
     
     final Handler mHandler = new Handler() {
         //public Handler() {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
new file mode 100644
index 0000000..39b63db
--- /dev/null
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -0,0 +1,1017 @@
+/*
+ * Copyright (C) 2012 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 com.android.server.am;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserId;
+import android.util.EventLog;
+import android.util.Slog;
+
+/**
+ * BROADCASTS
+ *
+ * We keep two broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts.
+ */
+public class BroadcastQueue {
+    static final String TAG = "BroadcastQueue";
+    static final String TAG_MU = ActivityManagerService.TAG_MU;
+    static final boolean DEBUG_BROADCAST = ActivityManagerService.DEBUG_BROADCAST;
+    static final boolean DEBUG_BROADCAST_LIGHT = ActivityManagerService.DEBUG_BROADCAST_LIGHT;
+    static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
+
+    static final int MAX_BROADCAST_HISTORY = 25;
+
+    final ActivityManagerService mService;
+
+    /**
+     * Recognizable moniker for this queue
+     */
+    final String mQueueName;
+
+    /**
+     * Timeout period for this queue's broadcasts
+     */
+    final long mTimeoutPeriod;
+
+    /**
+     * Lists of all active broadcasts that are to be executed immediately
+     * (without waiting for another broadcast to finish).  Currently this only
+     * contains broadcasts to registered receivers, to avoid spinning up
+     * a bunch of processes to execute IntentReceiver components.  Background-
+     * and foreground-priority broadcasts are queued separately.
+     */
+    final ArrayList<BroadcastRecord> mParallelBroadcasts
+            = new ArrayList<BroadcastRecord>();
+    /**
+     * List of all active broadcasts that are to be executed one at a time.
+     * The object at the top of the list is the currently activity broadcasts;
+     * those after it are waiting for the top to finish.  As with parallel
+     * broadcasts, separate background- and foreground-priority queues are
+     * maintained.
+     */
+    final ArrayList<BroadcastRecord> mOrderedBroadcasts
+            = new ArrayList<BroadcastRecord>();
+
+    /**
+     * Historical data of past broadcasts, for debugging.
+     */
+    final BroadcastRecord[] mBroadcastHistory
+            = new BroadcastRecord[MAX_BROADCAST_HISTORY];
+
+    /**
+     * Set when we current have a BROADCAST_INTENT_MSG in flight.
+     */
+    boolean mBroadcastsScheduled = false;
+
+    /**
+     * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+     */
+    boolean mPendingBroadcastTimeoutMessage;
+
+    /**
+     * Intent broadcasts that we have tried to start, but are
+     * waiting for the application's process to be created.  We only
+     * need one per scheduling class (instead of a list) because we always
+     * process broadcasts one at a time, so no others can be started while
+     * waiting for this one.
+     */
+    BroadcastRecord mPendingBroadcast = null;
+
+    /**
+     * The receiver index that is pending, to restart the broadcast if needed.
+     */
+    int mPendingBroadcastRecvIndex;
+
+    static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
+    static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
+
+    final Handler mHandler = new Handler() {
+        //public Handler() {
+        //    if (localLOGV) Slog.v(TAG, "Handler started!");
+        //}
+
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case BROADCAST_INTENT_MSG: {
+                    if (DEBUG_BROADCAST) Slog.v(
+                            TAG, "Received BROADCAST_INTENT_MSG");
+                    processNextBroadcast(true);
+                } break;
+                case BROADCAST_TIMEOUT_MSG: {
+                    synchronized (mService) {
+                        broadcastTimeoutLocked(true);
+                    }
+                } break;
+            }
+        }
+    };
+
+    private final class AppNotResponding implements Runnable {
+        private final ProcessRecord mApp;
+        private final String mAnnotation;
+
+        public AppNotResponding(ProcessRecord app, String annotation) {
+            mApp = app;
+            mAnnotation = annotation;
+        }
+
+        @Override
+        public void run() {
+            mService.appNotResponding(mApp, null, null, mAnnotation);
+        }
+    }
+
+    BroadcastQueue(ActivityManagerService service, String name, long timeoutPeriod) {
+        mService = service;
+        mQueueName = name;
+        mTimeoutPeriod = timeoutPeriod;
+    }
+
+    public boolean isPendingBroadcastProcessLocked(int pid) {
+        return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
+    }
+
+    public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
+        mParallelBroadcasts.add(r);
+    }
+
+    public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+        mOrderedBroadcasts.add(r);
+    }
+
+    public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
+        for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
+            if (r.intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "***** DROPPING PARALLEL ["
+                + mQueueName + "]: " + r.intent);
+                mParallelBroadcasts.set(i, r);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) {
+        for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
+            if (r.intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "***** DROPPING ORDERED ["
+                        + mQueueName + "]: " + r.intent);
+                mOrderedBroadcasts.set(i, r);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private final void processCurBroadcastLocked(BroadcastRecord r,
+            ProcessRecord app) throws RemoteException {
+        if (DEBUG_BROADCAST)  Slog.v(TAG,
+                "Process cur broadcast " + r + " for app " + app);
+        if (app.thread == null) {
+            throw new RemoteException();
+        }
+        r.receiver = app.thread.asBinder();
+        r.curApp = app;
+        app.curReceiver = r;
+        mService.updateLruProcessLocked(app, true, true);
+
+        // Tell the application to launch this receiver.
+        r.intent.setComponent(r.curComponent);
+
+        boolean started = false;
+        try {
+            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG,
+                    "Delivering to component " + r.curComponent
+                    + ": " + r);
+            mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
+                    mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
+                    r.resultCode, r.resultData, r.resultExtras, r.ordered);
+            if (DEBUG_BROADCAST)  Slog.v(TAG,
+                    "Process cur broadcast " + r + " DELIVERED for app " + app);
+            started = true;
+        } finally {
+            if (!started) {
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Process cur broadcast " + r + ": NOT STARTED!");
+                r.receiver = null;
+                r.curApp = null;
+                app.curReceiver = null;
+            }
+        }
+    }
+
+    public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+        boolean didSomething = false;
+        final BroadcastRecord br = mPendingBroadcast;
+        if (br != null && br.curApp.pid == app.pid) {
+            try {
+                mPendingBroadcast = null;
+                processCurBroadcastLocked(br, app);
+                didSomething = true;
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception in new application when starting receiver "
+                        + br.curComponent.flattenToShortString(), e);
+                logBroadcastReceiverDiscardLocked(br);
+                finishReceiverLocked(br, br.resultCode, br.resultData,
+                        br.resultExtras, br.resultAbort, true);
+                scheduleBroadcastsLocked();
+                // We need to reset the state if we fails to start the receiver.
+                br.state = BroadcastRecord.IDLE;
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+        return didSomething;
+    }
+
+    public void skipPendingBroadcastLocked(int pid) {
+        final BroadcastRecord br = mPendingBroadcast;
+        if (br != null && br.curApp.pid == pid) {
+            br.state = BroadcastRecord.IDLE;
+            br.nextReceiver = mPendingBroadcastRecvIndex;
+            mPendingBroadcast = null;
+            scheduleBroadcastsLocked();
+        }
+    }
+
+    public void skipCurrentReceiverLocked(ProcessRecord app) {
+        boolean reschedule = false;
+        BroadcastRecord r = app.curReceiver;
+        if (r != null) {
+            // The current broadcast is waiting for this app's receiver
+            // to be finished.  Looks like that's not going to happen, so
+            // let the broadcast continue.
+            logBroadcastReceiverDiscardLocked(r);
+            finishReceiverLocked(r, r.resultCode, r.resultData,
+                    r.resultExtras, r.resultAbort, true);
+            reschedule = true;
+        }
+
+        r = mPendingBroadcast;
+        if (r != null && r.curApp == app) {
+            if (DEBUG_BROADCAST) Slog.v(TAG,
+                    "[" + mQueueName + "] skip & discard pending app " + r);
+            logBroadcastReceiverDiscardLocked(r);
+            finishReceiverLocked(r, r.resultCode, r.resultData,
+                    r.resultExtras, r.resultAbort, true);
+            reschedule = true;
+        }
+        if (reschedule) {
+            scheduleBroadcastsLocked();
+        }
+    }
+
+    public void scheduleBroadcastsLocked() {
+        if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
+                + mQueueName + "]: current="
+                + mBroadcastsScheduled);
+
+        if (mBroadcastsScheduled) {
+            return;
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
+        mBroadcastsScheduled = true;
+    }
+
+    public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
+        if (mOrderedBroadcasts.size() > 0) {
+            final BroadcastRecord r = mOrderedBroadcasts.get(0);
+            if (r != null && r.receiver == receiver) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+    public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
+            String resultData, Bundle resultExtras, boolean resultAbort,
+            boolean explicit) {
+        int state = r.state;
+        r.state = BroadcastRecord.IDLE;
+        if (state == BroadcastRecord.IDLE) {
+            if (explicit) {
+                Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
+            }
+        }
+        r.receiver = null;
+        r.intent.setComponent(null);
+        if (r.curApp != null) {
+            r.curApp.curReceiver = null;
+        }
+        if (r.curFilter != null) {
+            r.curFilter.receiverList.curBroadcast = null;
+        }
+        r.curFilter = null;
+        r.curApp = null;
+        r.curComponent = null;
+        r.curReceiver = null;
+        mPendingBroadcast = null;
+
+        r.resultCode = resultCode;
+        r.resultData = resultData;
+        r.resultExtras = resultExtras;
+        r.resultAbort = resultAbort;
+
+        // We will process the next receiver right now if this is finishing
+        // an app receiver (which is always asynchronous) or after we have
+        // come back from calling a receiver.
+        return state == BroadcastRecord.APP_RECEIVE
+                || state == BroadcastRecord.CALL_DONE_RECEIVE;
+    }
+
+    private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
+            Intent intent, int resultCode, String data, Bundle extras,
+            boolean ordered, boolean sticky) throws RemoteException {
+        // Send the intent to the receiver asynchronously using one-way binder calls.
+        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, sticky);
+        } else {
+            receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
+        }
+    }
+
+    private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
+            BroadcastFilter filter, boolean ordered) {
+        boolean skip = false;
+        if (filter.requiredPermission != null) {
+            int perm = mService.checkComponentPermission(filter.requiredPermission,
+                    r.callingPid, r.callingUid, -1, true);
+            if (perm != PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Permission Denial: broadcasting "
+                        + r.intent.toString()
+                        + " from " + r.callerPackage + " (pid="
+                        + r.callingPid + ", uid=" + r.callingUid + ")"
+                        + " requires " + filter.requiredPermission
+                        + " due to registered receiver " + filter);
+                skip = true;
+            }
+        }
+        if (r.requiredPermission != null) {
+            int perm = mService.checkComponentPermission(r.requiredPermission,
+                    filter.receiverList.pid, filter.receiverList.uid, -1, true);
+            if (perm != PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Permission Denial: receiving "
+                        + r.intent.toString()
+                        + " to " + filter.receiverList.app
+                        + " (pid=" + filter.receiverList.pid
+                        + ", uid=" + filter.receiverList.uid + ")"
+                        + " requires " + r.requiredPermission
+                        + " due to sender " + r.callerPackage
+                        + " (uid " + r.callingUid + ")");
+                skip = true;
+            }
+        }
+
+        if (!skip) {
+            // If this is not being sent as an ordered broadcast, then we
+            // don't want to touch the fields that keep track of the current
+            // state of ordered broadcasts.
+            if (ordered) {
+                r.receiver = filter.receiverList.receiver.asBinder();
+                r.curFilter = filter;
+                filter.receiverList.curBroadcast = r;
+                r.state = BroadcastRecord.CALL_IN_RECEIVE;
+                if (filter.receiverList.app != null) {
+                    // Bump hosting application to no longer be in background
+                    // scheduling class.  Note that we can't do that if there
+                    // isn't an app...  but we can only be in that case for
+                    // things that directly call the IActivityManager API, which
+                    // are already core system stuff so don't matter for this.
+                    r.curApp = filter.receiverList.app;
+                    filter.receiverList.app.curReceiver = r;
+                    mService.updateOomAdjLocked();
+                }
+            }
+            try {
+                if (DEBUG_BROADCAST_LIGHT) {
+                    int seq = r.intent.getIntExtra("seq", -1);
+                    Slog.i(TAG, "Delivering to " + filter
+                            + " (seq=" + seq + "): " + r);
+                }
+                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+                    new Intent(r.intent), r.resultCode,
+                    r.resultData, r.resultExtras, r.ordered, r.initialSticky);
+                if (ordered) {
+                    r.state = BroadcastRecord.CALL_DONE_RECEIVE;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
+                if (ordered) {
+                    r.receiver = null;
+                    r.curFilter = null;
+                    filter.receiverList.curBroadcast = null;
+                    if (filter.receiverList.app != null) {
+                        filter.receiverList.app.curReceiver = null;
+                    }
+                }
+            }
+        }
+    }
+
+    final void processNextBroadcast(boolean fromMsg) {
+        synchronized(mService) {
+            BroadcastRecord r;
+
+            if (DEBUG_BROADCAST) Slog.v(TAG, "processNextBroadcast ["
+                    + mQueueName + "]: "
+                    + mParallelBroadcasts.size() + " broadcasts, "
+                    + mOrderedBroadcasts.size() + " ordered broadcasts");
+
+            mService.updateCpuStats();
+
+            if (fromMsg) {
+                mBroadcastsScheduled = false;
+            }
+
+            // First, deliver any non-serialized broadcasts right away.
+            while (mParallelBroadcasts.size() > 0) {
+                r = mParallelBroadcasts.remove(0);
+                r.dispatchTime = SystemClock.uptimeMillis();
+                r.dispatchClockTime = System.currentTimeMillis();
+                final int N = r.receivers.size();
+                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
+                        + mQueueName + "] " + r);
+                for (int i=0; i<N; i++) {
+                    Object target = r.receivers.get(i);
+                    if (DEBUG_BROADCAST)  Slog.v(TAG,
+                            "Delivering non-ordered on [" + mQueueName + "] to registered "
+                            + target + ": " + r);
+                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
+                }
+                addBroadcastToHistoryLocked(r);
+                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
+                        + mQueueName + "] " + r);
+            }
+
+            // Now take care of the next serialized one...
+
+            // If we are waiting for a process to come up to handle the next
+            // broadcast, then do nothing at this point.  Just in case, we
+            // check that the process we're waiting for still exists.
+            if (mPendingBroadcast != null) {
+                if (DEBUG_BROADCAST_LIGHT) {
+                    Slog.v(TAG, "processNextBroadcast ["
+                            + mQueueName + "]: waiting for "
+                            + mPendingBroadcast.curApp);
+                }
+
+                boolean isDead;
+                synchronized (mService.mPidsSelfLocked) {
+                    isDead = (mService.mPidsSelfLocked.get(
+                            mPendingBroadcast.curApp.pid) == null);
+                }
+                if (!isDead) {
+                    // It's still alive, so keep waiting
+                    return;
+                } else {
+                    Slog.w(TAG, "pending app  ["
+                            + mQueueName + "]" + mPendingBroadcast.curApp
+                            + " died before responding to broadcast");
+                    mPendingBroadcast.state = BroadcastRecord.IDLE;
+                    mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
+                    mPendingBroadcast = null;
+                }
+            }
+
+            boolean looped = false;
+            
+            do {
+                if (mOrderedBroadcasts.size() == 0) {
+                    // No more broadcasts pending, so all done!
+                    mService.scheduleAppGcsLocked();
+                    if (looped) {
+                        // If we had finished the last ordered broadcast, then
+                        // make sure all processes have correct oom and sched
+                        // adjustments.
+                        mService.updateOomAdjLocked();
+                    }
+                    return;
+                }
+                r = mOrderedBroadcasts.get(0);
+                boolean forceReceive = false;
+
+                // Ensure that even if something goes awry with the timeout
+                // detection, we catch "hung" broadcasts here, discard them,
+                // and continue to make progress.
+                //
+                // This is only done if the system is ready so that PRE_BOOT_COMPLETED
+                // receivers don't get executed with timeouts. They're intended for
+                // one time heavy lifting after system upgrades and can take
+                // significant amounts of time.
+                int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+                if (mService.mProcessesReady && r.dispatchTime > 0) {
+                    long now = SystemClock.uptimeMillis();
+                    if ((numReceivers > 0) &&
+                            (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
+                        Slog.w(TAG, "Hung broadcast ["
+                                + mQueueName + "] discarded after timeout failure:"
+                                + " now=" + now
+                                + " dispatchTime=" + r.dispatchTime
+                                + " startTime=" + r.receiverTime
+                                + " intent=" + r.intent
+                                + " numReceivers=" + numReceivers
+                                + " nextReceiver=" + r.nextReceiver
+                                + " state=" + r.state);
+                        broadcastTimeoutLocked(false); // forcibly finish this broadcast
+                        forceReceive = true;
+                        r.state = BroadcastRecord.IDLE;
+                    }
+                }
+
+                if (r.state != BroadcastRecord.IDLE) {
+                    if (DEBUG_BROADCAST) Slog.d(TAG,
+                            "processNextBroadcast("
+                            + mQueueName + ") called when not idle (state="
+                            + r.state + ")");
+                    return;
+                }
+
+                if (r.receivers == null || r.nextReceiver >= numReceivers
+                        || r.resultAbort || forceReceive) {
+                    // No more receivers for this broadcast!  Send the final
+                    // result if requested...
+                    if (r.resultTo != null) {
+                        try {
+                            if (DEBUG_BROADCAST) {
+                                int seq = r.intent.getIntExtra("seq", -1);
+                                Slog.i(TAG, "Finishing broadcast ["
+                                        + mQueueName + "] " + r.intent.getAction()
+                                        + " seq=" + seq + " app=" + r.callerApp);
+                            }
+                            performReceiveLocked(r.callerApp, r.resultTo,
+                                new Intent(r.intent), r.resultCode,
+                                r.resultData, r.resultExtras, false, false);
+                            // Set this to null so that the reference
+                            // (local and remote) isnt kept in the mBroadcastHistory.
+                            r.resultTo = null;
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Failure ["
+                                    + mQueueName + "] sending broadcast result of "
+                                    + r.intent, e);
+                        }
+                    }
+
+                    if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
+                    cancelBroadcastTimeoutLocked();
+
+                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
+                            + r);
+
+                    // ... and on to the next...
+                    addBroadcastToHistoryLocked(r);
+                    mOrderedBroadcasts.remove(0);
+                    r = null;
+                    looped = true;
+                    continue;
+                }
+            } while (r == null);
+
+            // Get the next receiver...
+            int recIdx = r.nextReceiver++;
+
+            // Keep track of when this receiver started, and make sure there
+            // is a timeout message pending to kill it if need be.
+            r.receiverTime = SystemClock.uptimeMillis();
+            if (recIdx == 0) {
+                r.dispatchTime = r.receiverTime;
+                r.dispatchClockTime = System.currentTimeMillis();
+                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast ["
+                        + mQueueName + "] " + r);
+            }
+            if (! mPendingBroadcastTimeoutMessage) {
+                long timeoutTime = r.receiverTime + mTimeoutPeriod;
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "Submitting BROADCAST_TIMEOUT_MSG ["
+                        + mQueueName + "] for " + r + " at " + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
+            }
+
+            Object nextReceiver = r.receivers.get(recIdx);
+            if (nextReceiver instanceof BroadcastFilter) {
+                // Simple case: this is a registered receiver who gets
+                // a direct call.
+                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Delivering ordered ["
+                        + mQueueName + "] to registered "
+                        + filter + ": " + r);
+                deliverToRegisteredReceiverLocked(r, filter, r.ordered);
+                if (r.receiver == null || !r.ordered) {
+                    // The receiver has already finished, so schedule to
+                    // process the next one.
+                    if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing ["
+                            + mQueueName + "]: ordered="
+                            + r.ordered + " receiver=" + r.receiver);
+                    r.state = BroadcastRecord.IDLE;
+                    scheduleBroadcastsLocked();
+                }
+                return;
+            }
+
+            // Hard case: need to instantiate the receiver, possibly
+            // starting its application process to host it.
+
+            ResolveInfo info =
+                (ResolveInfo)nextReceiver;
+
+            boolean skip = false;
+            int perm = mService.checkComponentPermission(info.activityInfo.permission,
+                    r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
+                    info.activityInfo.exported);
+            if (perm != PackageManager.PERMISSION_GRANTED) {
+                if (!info.activityInfo.exported) {
+                    Slog.w(TAG, "Permission Denial: broadcasting "
+                            + r.intent.toString()
+                            + " from " + r.callerPackage + " (pid=" + r.callingPid
+                            + ", uid=" + r.callingUid + ")"
+                            + " is not exported from uid " + info.activityInfo.applicationInfo.uid
+                            + " due to receiver " + info.activityInfo.packageName
+                            + "/" + info.activityInfo.name);
+                } else {
+                    Slog.w(TAG, "Permission Denial: broadcasting "
+                            + r.intent.toString()
+                            + " from " + r.callerPackage + " (pid=" + r.callingPid
+                            + ", uid=" + r.callingUid + ")"
+                            + " requires " + info.activityInfo.permission
+                            + " due to receiver " + info.activityInfo.packageName
+                            + "/" + info.activityInfo.name);
+                }
+                skip = true;
+            }
+            if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
+                r.requiredPermission != null) {
+                try {
+                    perm = AppGlobals.getPackageManager().
+                            checkPermission(r.requiredPermission,
+                                    info.activityInfo.applicationInfo.packageName);
+                } catch (RemoteException e) {
+                    perm = PackageManager.PERMISSION_DENIED;
+                }
+                if (perm != PackageManager.PERMISSION_GRANTED) {
+                    Slog.w(TAG, "Permission Denial: receiving "
+                            + r.intent + " to "
+                            + info.activityInfo.applicationInfo.packageName
+                            + " requires " + r.requiredPermission
+                            + " due to sender " + r.callerPackage
+                            + " (uid " + r.callingUid + ")");
+                    skip = true;
+                }
+            }
+            if (r.curApp != null && r.curApp.crashing) {
+                // If the target process is crashing, just skip it.
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Skipping deliver ordered ["
+                        + mQueueName + "] " + r + " to " + r.curApp
+                        + ": process crashing");
+                skip = true;
+            }
+
+            if (skip) {
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Skipping delivery of ordered ["
+                        + mQueueName + "] " + r + " for whatever reason");
+                r.receiver = null;
+                r.curFilter = null;
+                r.state = BroadcastRecord.IDLE;
+                scheduleBroadcastsLocked();
+                return;
+            }
+
+            r.state = BroadcastRecord.APP_RECEIVE;
+            String targetProcess = info.activityInfo.processName;
+            r.curComponent = new ComponentName(
+                    info.activityInfo.applicationInfo.packageName,
+                    info.activityInfo.name);
+            if (r.callingUid != Process.SYSTEM_UID) {
+                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, UserId
+                        .getUserId(r.callingUid));
+            }
+            r.curReceiver = info.activityInfo;
+            if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+                Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+                        + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+                        + info.activityInfo.applicationInfo.uid);
+            }
+
+            // Broadcast is being executed, its package can't be stopped.
+            try {
+                AppGlobals.getPackageManager().setPackageStoppedState(
+                        r.curComponent.getPackageName(), false);
+            } catch (RemoteException e) {
+            } catch (IllegalArgumentException e) {
+                Slog.w(TAG, "Failed trying to unstop package "
+                        + r.curComponent.getPackageName() + ": " + e);
+            }
+
+            // Is this receiver's application already running?
+            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
+                    info.activityInfo.applicationInfo.uid);
+            if (app != null && app.thread != null) {
+                try {
+                    app.addPackage(info.activityInfo.packageName);
+                    processCurBroadcastLocked(r, app);
+                    return;
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Exception when sending broadcast to "
+                          + r.curComponent, e);
+                }
+
+                // If a dead object exception was thrown -- fall through to
+                // restart the application.
+            }
+
+            // Not running -- get it started, to be executed when the app comes up.
+            if (DEBUG_BROADCAST)  Slog.v(TAG,
+                    "Need to start app ["
+                    + mQueueName + "] " + targetProcess + " for broadcast " + r);
+            if ((r.curApp=mService.startProcessLocked(targetProcess,
+                    info.activityInfo.applicationInfo, true,
+                    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
+                    "broadcast", r.curComponent,
+                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false))
+                            == null) {
+                // Ah, this recipient is unavailable.  Finish it if necessary,
+                // and mark the broadcast record as ready for the next.
+                Slog.w(TAG, "Unable to launch app "
+                        + info.activityInfo.applicationInfo.packageName + "/"
+                        + info.activityInfo.applicationInfo.uid + " for broadcast "
+                        + r.intent + ": process is bad");
+                logBroadcastReceiverDiscardLocked(r);
+                finishReceiverLocked(r, r.resultCode, r.resultData,
+                        r.resultExtras, r.resultAbort, true);
+                scheduleBroadcastsLocked();
+                r.state = BroadcastRecord.IDLE;
+                return;
+            }
+
+            mPendingBroadcast = r;
+            mPendingBroadcastRecvIndex = recIdx;
+        }
+    }
+
+    final void setBroadcastTimeoutLocked(long timeoutTime) {
+        if (! mPendingBroadcastTimeoutMessage) {
+            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
+            mHandler.sendMessageAtTime(msg, timeoutTime);
+            mPendingBroadcastTimeoutMessage = true;
+        }
+    }
+
+    final void cancelBroadcastTimeoutLocked() {
+        if (mPendingBroadcastTimeoutMessage) {
+            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
+            mPendingBroadcastTimeoutMessage = false;
+        }
+    }
+
+    final void broadcastTimeoutLocked(boolean fromMsg) {
+        if (fromMsg) {
+            mPendingBroadcastTimeoutMessage = false;
+        }
+
+        if (mOrderedBroadcasts.size() == 0) {
+            return;
+        }
+
+        long now = SystemClock.uptimeMillis();
+        BroadcastRecord r = mOrderedBroadcasts.get(0);
+        if (fromMsg) {
+            if (mService.mDidDexOpt) {
+                // Delay timeouts until dexopt finishes.
+                mService.mDidDexOpt = false;
+                long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+            if (!mService.mProcessesReady) {
+                // Only process broadcast timeouts if the system is ready. That way
+                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
+                // to do heavy lifting for system up.
+                return;
+            }
+
+            long timeoutTime = r.receiverTime + mTimeoutPeriod;
+            if (timeoutTime > now) {
+                // We can observe premature timeouts because we do not cancel and reset the
+                // broadcast timeout message after each receiver finishes.  Instead, we set up
+                // an initial timeout then kick it down the road a little further as needed
+                // when it expires.
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "Premature timeout ["
+                        + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+                        + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+        }
+
+        Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+                + ", started " + (now - r.receiverTime) + "ms ago");
+        r.receiverTime = now;
+        r.anrCount++;
+
+        // Current receiver has passed its expiration date.
+        if (r.nextReceiver <= 0) {
+            Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
+            return;
+        }
+
+        ProcessRecord app = null;
+        String anrMessage = null;
+
+        Object curReceiver = r.receivers.get(r.nextReceiver-1);
+        Slog.w(TAG, "Receiver during timeout: " + curReceiver);
+        logBroadcastReceiverDiscardLocked(r);
+        if (curReceiver instanceof BroadcastFilter) {
+            BroadcastFilter bf = (BroadcastFilter)curReceiver;
+            if (bf.receiverList.pid != 0
+                    && bf.receiverList.pid != ActivityManagerService.MY_PID) {
+                synchronized (mService.mPidsSelfLocked) {
+                    app = mService.mPidsSelfLocked.get(
+                            bf.receiverList.pid);
+                }
+            }
+        } else {
+            app = r.curApp;
+        }
+
+        if (app != null) {
+            anrMessage = "Broadcast of " + r.intent.toString();
+        }
+
+        if (mPendingBroadcast == r) {
+            mPendingBroadcast = null;
+        }
+
+        // Move on to the next receiver.
+        finishReceiverLocked(r, r.resultCode, r.resultData,
+                r.resultExtras, r.resultAbort, true);
+        scheduleBroadcastsLocked();
+
+        if (anrMessage != null) {
+            // Post the ANR to the handler since we do not want to process ANRs while
+            // potentially holding our lock.
+            mHandler.post(new AppNotResponding(app, anrMessage));
+        }
+    }
+
+    private final void addBroadcastToHistoryLocked(BroadcastRecord r) {
+        if (r.callingUid < 0) {
+            // This was from a registerReceiver() call; ignore it.
+            return;
+        }
+        System.arraycopy(mBroadcastHistory, 0, mBroadcastHistory, 1,
+                MAX_BROADCAST_HISTORY-1);
+        r.finishTime = SystemClock.uptimeMillis();
+        mBroadcastHistory[0] = r;
+    }
+
+    final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
+        if (r.nextReceiver > 0) {
+            Object curReceiver = r.receivers.get(r.nextReceiver-1);
+            if (curReceiver instanceof BroadcastFilter) {
+                BroadcastFilter bf = (BroadcastFilter) curReceiver;
+                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
+                        System.identityHashCode(r),
+                        r.intent.getAction(),
+                        r.nextReceiver - 1,
+                        System.identityHashCode(bf));
+            } else {
+                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+                        System.identityHashCode(r),
+                        r.intent.getAction(),
+                        r.nextReceiver - 1,
+                        ((ResolveInfo)curReceiver).toString());
+            }
+        } else {
+            Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
+                    + r);
+            EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+                    System.identityHashCode(r),
+                    r.intent.getAction(),
+                    r.nextReceiver,
+                    "NONE");
+        }
+    }
+
+    final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+        if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
+                || mPendingBroadcast != null) {
+            boolean printed = false;
+            for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
+                BroadcastRecord br = mParallelBroadcasts.get(i);
+                if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
+                    continue;
+                }
+                if (!printed) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    printed = true;
+                    pw.println("  Active broadcasts [" + mQueueName + "]:");
+                }
+                pw.println("  Broadcast #" + i + ":");
+                br.dump(pw, "    ");
+            }
+            printed = false;
+            needSep = true;
+            for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
+                BroadcastRecord br = mOrderedBroadcasts.get(i);
+                if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
+                    continue;
+                }
+                if (!printed) {
+                    if (needSep) {
+                        pw.println();
+                    }
+                    needSep = true;
+                    pw.println("  Active ordered broadcasts [" + mQueueName + "]:");
+                }
+                pw.println("  Ordered Broadcast #" + i + ":");
+                mOrderedBroadcasts.get(i).dump(pw, "    ");
+            }
+            if (dumpPackage == null || (mPendingBroadcast != null
+                    && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
+                if (needSep) {
+                    pw.println();
+                }
+                pw.println("  Pending broadcast [" + mQueueName + "]:");
+                if (mPendingBroadcast != null) {
+                    mPendingBroadcast.dump(pw, "    ");
+                } else {
+                    pw.println("    (null)");
+                }
+                needSep = true;
+            }
+        }
+
+        boolean printed = false;
+        for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
+            BroadcastRecord r = mBroadcastHistory[i];
+            if (r == null) {
+                break;
+            }
+            if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
+                continue;
+            }
+            if (!printed) {
+                if (needSep) {
+                    pw.println();
+                }
+                needSep = true;
+                pw.println("  Historical broadcasts [" + mQueueName + "]:");
+                printed = true;
+            }
+            if (dumpAll) {
+                pw.print("  Historical Broadcast #"); pw.print(i); pw.println(":");
+                r.dump(pw, "    ");
+            } else {
+                if (i >= 50) {
+                    pw.println("  ...");
+                    break;
+                }
+                pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
+            }
+        }
+
+        return needSep;
+    }
+}
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 6738e4f..dd560fc 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -59,7 +59,7 @@
     IBinder receiver;       // who is currently running, null if none.
     int state;
     int anrCount;           // has this broadcast record hit any ANRs?
-    ActivityManagerService.BroadcastQueue queue;   // the outbound queue handling this broadcast
+    BroadcastQueue queue;   // the outbound queue handling this broadcast
 
     static final int IDLE = 0;
     static final int APP_RECEIVE = 1;
@@ -162,7 +162,7 @@
         }
     }
 
-    BroadcastRecord(ActivityManagerService.BroadcastQueue _queue,
+    BroadcastRecord(BroadcastQueue _queue,
             Intent _intent, ProcessRecord _callerApp, String _callerPackage,
             int _callingPid, int _callingUid, String _requiredPermission,
             List _receivers, IIntentReceiver _resultTo, int _resultCode,
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index f6564862..cd72202 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -41,7 +41,7 @@
 
     private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
 
-    private static final int MSG_WRITE = 1;
+    private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
 
     private final Handler mHandler = new Handler() {
         @Override public void handleMessage(Message msg) {