The Problem:
In an Android application, upon updating the targetSDKVersion to 34, a crash is encountered with the play core library. The error message indicates a security exception with the registration of a receiver using LocalBroadcastManager. The specific error is ‘One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn’t being registered exclusively for system broadcasts’. Determine the cause of the issue and suggest a solution to resolve the SecurityException.
The Solutions:
Solution 1: Specify the export flag when registering context-registered receivers
In Android 14, you need to specify the export flag when registering context-registered receivers. This is necessary to indicate whether the receiver should be exported to all other apps on the device or not. You can use either `RECEIVER_EXPORTED` or `RECEIVER_NOT_EXPORTED` as the export flag.
If your `MessageReceiver` is supposed to be called only by the components within your app, then you should use `RECEIVER_NOT_EXPORTED`. If it is supposed to be accessible to components beyond your own app – use `RECEIVER_EXPORTED`.
To register the receiver with the export flag, use the `ContextCompat` class instead of `LocalBroadcastManager`. The code should look like this:
ContextCompat.registerReceiver(
this,
mMessageReceiver,
filter,
ContextCompat.RECEIVER_NOT_EXPORTED
)
This will prevent the error about `One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts.`
However, note that if you are using `RECEIVER_NOT_EXPORTED` and you are calling the `MessageReceiver` with an implicit intent, you need to make the `Intent` explicit. This means setting the package name of the app in the `Intent`. Otherwise, the intent will not be received on Android 14+.
“`kotlin
context.sendBroadcast(
Intent(YOUR_APP_ACTION).apply {
setPackage(context.packageName)
}
)
“`
Solution 2: Specifying receiver flag
To resolve the issue, you need to specify a flag when registering the receiver to indicate whether it should be exported to all other apps on the device.
This can be done by following these steps:
1. Create an instance of BroadcastReceiver:
val br: BroadcastReceiver = MyBroadcastReceiver()
2. Create an instance of IntentFilter:
val filter = IntentFilter(YOUR_BROADCAST_FILTER)
3. Choose whether the broadcast receiver should be exported and visible to other apps on the device:
val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
ContextCompat.RECEIVER_EXPORTED
} else {
ContextCompat.RECEIVER_NOT_EXPORTED
}
4. Register the receiver by calling registerReceiver():
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
Caution: If the broadcast receiver is exported, other apps could send unprotected broadcasts to your app.
Q&A
I updated my targetSDKVersion to 34, and after that, I started getting crashes with the LocalBroadcastManager with the following error message: java.lang.SecurityException: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED (com.google.android.play:core@@1.10.3:3)
Starting from Android 14, context-registered receivers must specify the exported flag using RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED. This requirement applies to the AndroidX Play Core library as well.
What is the purpose of the RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED flags?
The RECEIVER_EXPORTED flag indicates that the receiver should be exported and visible to other apps on the device, while the RECEIVER_NOT_EXPORTED flag indicates that the receiver is listening only for broadcasts sent by your app.