I'm trying to send a broadcast to a BroadcastReceiver which is present in another app, using the following code:
val filter = IntentFilter()
filter.addAction("android.intent.action.MAIN")
val cmdIntent = Intent()
cmdIntent.setComponent(
ComponentName(
pkgName,
pkgName + ".CmdReceiver"
)
)
cmdIntent.flags = Intent.FLAG_RECEIVER_FOREGROUND
cmdIntent.flags = Intent.FLAG_INCLUDE_STOPPED_PACKAGES
sendBroadcast(cmdIntent)
Here pkgName is the package name of the target app, which will be defined by the user. How do I check if the .CmdReceiver BroadcastReceiver actually exists in the target app or not, and then execute a function called showDialogNotFound() if it does not exist?
EDIT:
I have implemented it like this:
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
try {
val componentName = ComponentName(pkgName, pkgName + ".CmdReceiver")
val receiverInfo = packageManager.getReceiverInfo(componentName, 0)
Log.d("MainActivity", "BroadcastReceiver exists!")
showDialogFound()
} catch (e: PackageManager.NameNotFoundException) {
showDialogNotFound()
Log.e("MainActivity", e.toString())
}
}
else {
val filter = IntentFilter()
filter.addAction("android.intent.action.MAIN")
val cmdIntent = Intent()
cmdIntent.setComponent(
ComponentName(
pkgName,
pkgName + ".CmdReceiver"
)
)
cmdIntent.flags = Intent.FLAG_RECEIVER_FOREGROUND
cmdIntent.flags = Intent.FLAG_INCLUDE_STOPPED_PACKAGES
cmdIntent.putExtra("isOrderedBroadcast", true)
sendOrderedBroadcast(cmdIntent, null, object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val result = getResultCode()
val resultData = getResultData()
if (result == -1 && resultData == "ReceiverFound") {
Log.d("MainActivity", "BroadcastReceiver exists!")
showDialogFound()
} else {
showDialogNotFound()
}
}
}, null, Activity.RESULT_CANCELED, null, null)
}
Also in the onReceive of CmdReceiver (it's in Java):
Boolean isOrderedBroadcast = intent.getBooleanExtra("isOrderedBroadcast", false);
if (isOrderedBroadcast) {
setResultCode(-1);
setResultData("ReceiverFound");
}
On Android 10 and below, it's simple and works flawlessly, as apps can directly retrieve the list of broadcast receivers in other apps through PackageManager. However this doesn't work in newer Android versions, probably due to new security restrictions. So, I tried the "result acknowledgement" method as mentioned in @ruby6221's answer, but it still executes showDialogNotFound() even when CmdReceiver exists.
What am I doing wrong?
And what was the meaning of the downvote?
I solved it like this, by directly querying the package manager for the presence of the CmdReceiver broadcast receiver in the target app.
try {
val componentName = ComponentName(pkgName, pkgName + ".CmdReceiver")
val receiverInfo = packageManager.getReceiverInfo(componentName, 0)
Log.d("MainActivity", "BroadcastReceiver exists!")
showDialogFound()
} catch (e: PackageManager.NameNotFoundException) {
showDialogNotFound()
Log.e("MainActivity", e.toString())
}
Also, a query needs to be added to AndroidManifest.xml if the app is for Android 11+:
<queries>
<package android:name="com.example.myapp"/>
</queries>
(Replace com.example.myapp with the package name of the target app)
If you do not know the specific package name, you can instead add the QUERY_ALL_PACKAGES permission:
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
This is however not recommended, as it can cause privacy and security issues.
In my case, I had to add the QUERY_ALL_PACKAGES permission, because the package name of the target app will be taken from the user's input. So it is not a fixed package name. My app also does not connect to the internet, which reduces the privacy and security risks.
The limitation of this approach is that only the Broadcast Receivers which are registered in the AndroidManifest.xml of the target app can be queried.