AOSP Framework & Internals
3 min read

BroadcastReceiver

Learn how Android's publish-subscribe mechanism allows apps to respond to system-wide events.

A BroadcastReceiver is an Android component that allows an application to register for system or application events. It acts as a system-wide publish-subscribe mechanism.

When the battery drops to 15%, the OS broadcasts an ACTION_BATTERY_LOW intent. Any app that has registered a receiver for this intent will instantly wake up and execute its code.

Types of Broadcasts

1. Normal (Unordered) Broadcasts

Sent via sendBroadcast(). These are completely asynchronous. All registered receivers execute simultaneously, in an undefined order. This is fast and efficient, but a receiver cannot cancel the broadcast or pass data to the next receiver.

2. Ordered Broadcasts

Sent via sendOrderedBroadcast(). These are delivered to one receiver at a time. The order is determined by the android:priority attribute in the manifest. A high-priority receiver (like an SMS spam blocker) can intercept the broadcast, process it, and completely abort it using abortBroadcast(), preventing lower-priority receivers from ever seeing the event.

3. Sticky Broadcasts (Deprecated)

Historically, sticky broadcasts remained in memory after they were sent. If you registered a receiver after the broadcast fired, you would still receive the last known value (e.g., ACTION_BATTERY_CHANGED). These caused massive security and memory leaks and have been strictly deprecated in modern Android.

Static vs. Dynamic Registration

  • Static Registration: Defined explicitly in the AndroidManifest.xml. Historically, if a static broadcast fired, the OS would force-boot the app process if it was dead. Due to massive battery drain (the "Boot Storm"), modern Android severely restricts which intents can be statically registered.
  • Dynamic Registration: Registered in Java/Kotlin code using Context.registerReceiver(). The receiver only lives as long as the component that registered it (e.g., if registered in an Activity's onResume, it stops receiving when onPause is called).
// Example of dynamically registering a receiver for Airplane Mode changes
BroadcastReceiver airplaneModeReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
        Log.d("Broadcast", "Airplane mode changed: " + isAirplaneModeOn);
    }
};

IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
context.registerReceiver(airplaneModeReceiver, filter);

The Broadcast Flow (AMS)

When you call sendBroadcast(), the intent does not go directly to the receiver.

  1. The app sends the Intent to the ActivityManagerService (AMS) via Binder.
  2. The AMS checks the Intent against its massive internal dictionary of registered receivers (the BroadcastQueue).
  3. The AMS checks permissions (e.g., does the sender have permission to send? Does the receiver have permission to receive?).
  4. The AMS dispatches the Intent sequentially or asynchronously back down to the target apps via Binder, executing their onReceive() methods on the main UI thread.
# Platform engineers can monitor the active broadcast queues and historical dispatch times
adb shell dumpsys activity broadcasts