Broadcast intents are one of the major building blocks of the Android platform, generally intended for asynchronous notification of events. There are three flavors of intents that can be broadcast:
And there are there two ways to receive these intents:
Context.registerReceiver()
methods) are dynamically requested by a running app to receive intents. These requests are only maintained while the process is running, and are discarded at process death.<receiver>
tag in AndroidManifest.xml
) are statically requested by an app to receive intents. These requests are delivered regardless of process running state, and have the ability to cold-start a process that isn't currently running.The design of BroadcastQueueModernImpl
is centered around maintaining a separate BroadcastProcessQueue
instance for each potential process on the device. At this level, a process refers to the android:process
attributes defined in AndroidManifest.xml
files, which means it can be defined and populated regardless of the process state. (For example, a given android:process
can have multiple ProcessRecord
/PIDs defined as it's launched, killed, and relaunched over long periods of time.)
Each per-process queue has the concept of a runnable at timestamp when it's next eligible for execution, and that value can be influenced by a wide range of policies, such as:
Each per-process queue represents a single remote ApplicationThread
, and we only dispatch a single broadcast at a time to each process to ensure developers see consistent ordering of broadcast events. The flexible runnable at policies above mean that no inter-process ordering guarantees are provided, except for those explicitly provided by “ordered” or “prioritized” broadcasts.
Given a collection of per-process queues with valid runnable at timestamps, BroadcastQueueModernImpl is then willing to promote those runnable queues into a running state. We choose the next per-process queue to promote based on the sorted ordering of the runnable at timestamps, selecting the longest-waiting process first, which aims to reduce overall broadcast dispatch latency.
To preserve system health, at most BroadcastConstants.MAX_RUNNING_PROCESS_QUEUES
processes are allowed to be in the running state at any given time, and at most one process is allowed to be cold started at any given time. (For background, cold starting a process by forking and specializing the zygote is a relatively heavy operation, so limiting ourselves to a single pending cold start reduces system-wide resource contention.)
After each broadcast is dispatched to a given process, we consider dispatching any additional pending broadcasts to that process, aimed at batching dispatch to better amortize the cost of OOM adjustments.
Careful attention is given to several types of potential resource starvation, along with the mechanisms of mitigation:
BroadcastConstants.MAX_PENDING_BROADCASTS
bypassing any delays when the queue grows too large.BroadcastConstants.MAX_RUNNING_ACTIVE_BROADCASTS
being used to temporarily “retire” a running process to give other processes a chance to run.mPendingUrgent
queue of urgent events, which we prefer to dispatch before the normal mPending
queue.