Skip to main content
Android Broadcast Receivers 101 
  • Android
  • How Tos

Android Broadcast Receivers 101 

David Lodge

02 Sep 2025 6 Min Read

TL;DR 

  • Broadcast receivers react to system and app events such as boot, charging, or headset plug-ins. 
  • Exported receivers may increase the attack surface of your app 
  • Usually defined in the manifest, although it can be created at runtime. 
  • From Android 8.0 (API 26), there are futher restrictions in implicit receivers. 
  • OEM secret codes can open hidden Activities or privileged menus. Treat them as a real attack surface 

Introduction  

This is the next post in our Android 101 series. Today we are looking at broadcast receivers. If you are catching up, start with Android services 101 for background components and scheduling, then Android content providers 101 for shared data surfaces and permissions. 

What a broadcast receiver is 

Next in the list of Android components is the broadcast receiver. This component is designed to, well, be a receiver for broadcasts! Broadcasts are sent out when something interesting happens to the operating system or an app, such as the phone being unlocked or starting to charge. 

Implicit and explicit broadcasts, and intents 

Broadcast events can be sent out by the system or a specific app and can be implicit (broadcast to all apps) or explicit (to a specific app). It’s counter-intuitive that you can broadcast to a single destination, but, shrug. The broadcast events are performed by sending intents, which I will write about in a later blog. 

The receiver can be defined in the manifest using the <receiver> tag, although this is only for explicit and some implicit broadcasts, or it can be programmatically created in the context of an activity or service for any broadcast. This does mean that it can only be received while the object exists. 

Enumerating receivers  

An app’s receivers can be enumerated from the manifest, although this isn’t perfect, as most implicit receivers will be programmatically added. You can reverse the app’s code, looking for any code that instances BroadcastReceiver, or use dumpsys activity broadcasts, using -p to restrict the package: 

Example: dumpsys output from Chrome 

S111:/ $ dumpsys activity -p com.android.chrome broadcasts | head -20 

[dump_debug] dumpAppId:10159, dumpPackage:com.android.chrome 

ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts) 

  Registered Receivers: 

  * ReceiverList{586fdc9 18659 com.android.chrome/10159/u0 remote:9f249d0} 

    app=18659:com.android.chrome/u0a159 pid=18659 uid=10159 user=0 

    Filter #0: BroadcastFilter{e437cce} 

      Action: "android.hardware.usb.action.USB_DEVICE_ATTACHED" 

      Action: "android.hardware.usb.action.USB_DEVICE_DETACHED" 

  * ReceiverList{3483a82 18659 com.android.chrome/10159/u0 remote:99e11cd} 

    app=18659:com.android.chrome/u0a159 pid=18659 uid=10159 user=0 

    Filter #0: BroadcastFilter{a20b593} 

      Action: "android.intent.action.HEADSET_PLUG" 

  * ReceiverList{fb3f8d2 18659 com.android.chrome/10159/u0 remote:fa3975d} 

    app=18659:com.android.chrome/u0a159 pid=18659 uid=10159 user=0 

    Filter #0: BroadcastFilter{9dd1da3} 

      Action: "android.intent.action.BATTERY_CHANGED" 

Sending broadcasts 

Sending a broadcast can be sent with sendBroadcast(Intent), which sends a “Normal Broadcast” to all receivers in an undefined order, or sendOrderedBroadcast(Intent, String) which sends in an ordered fashion, controlled by the priority of the intent-filter on the receiver. 

They can be sent from the Android command line through the am broadcast program.  

What are the risks? 

Like any constantly running process, there are risks about handling any received data, especially if it can run code or manipulate files in the app’s sandbox. This is more pertinent with receivers, due to their purpose. 

Manifest examples and code walkthrough 

We can easily find receivers in the manifest, marked (as I said above) by the <receiver> tag or by using dumpsys activity broadcasts. Then a bit of reverse engineering is needed. In the manifest a receiver definition looks like: 

The first shows an export receiver that is intended to receive a BOOT_COMPLETED intent. The second is a special type of receiver that I’ll go into later.Looking at the code for the first receiver: com.mediatek.camera.DisableCameraReceiver we can see that it extends the standard BroadcastReceiver class. It  handles all of its messages in the onReceive() method: 

This just receives the message and then initialises the camera as a background task, but there’s no data input, so there’s no injection point here. 

Let’s look at that other receiver: android.provider.Telephony.SECRET_CODE is a special intent action. You may have heard of special codes within the Android dialer [sic], such as *#06# to show your IMEI number. This intent is how apps can extend this feature with more secret codes: just implement a broadcast receiver with an action of android.provider.Telephony.SECRET_CODE and data of the secret code and your receiver will get the intent from the dailer. 

To use the code, it needs to be surrounded by *#*# and #*#* with the number in the middle, which can be found in the in app’s manifest, in the data tag of the intent-filter for an android.provider.Telephony.SECRET_CODE intent, for example *#*#3646633#*#*. This code on this device will bring up an EngineerMode menu as we can see in the below code, it extends BroadcastReceiver, implements onReceive and sends an intent to start the EngineerMode activity. 

Complications on modern Android 

With changes to Android since Android 8, broadcast receivers can’t be specified for implicit broadcasts in the manifest. This is to reduce battery use of background receivers. Intents can still be sent, but they must be explicit or to special system actions, such as ACTION_BOOT_COMPLETED. 

If an intent is sent to an implicit broadcast specified in the manifest, then the system log will include a message like: 

06-11 12:14:00.778  9801  9839 W BroadcastQueue: Background execution not allowed: receiving Intent { act=com.mediatek.engineermode.lte.cmd_start flg=0x400010 } to com.mediatek.engineermode/.lte.CommandToolReceiver 

Apps may work around this by programmatically registering any receivers. 

Conclusion 

Broadcast Receivers are a rarely used, but important component of the Android system, when written to receive system broadcasts, they can perform actions when an important event happens (e.g. a reboot, or installing a new package). 

Adding SECRET_CODES to the dialer is something that will occasionally be seen on OEM and system apps, but should be rare with user-level apps. 

That said it’s always worthwhile checking an app’s manifest and performing a dumpsys to look for any receivers when reviewing an app.