Background
Historically, Android Custom permissions have been a mess and were install order dependent, which was known to expose vulnerabilities.
Prior to API 21, there was an unsettling workaround whereby declaring the custom permission of another application in your Manifest, granted the permission... However, since API 21, only one application can declare a custom permission and the installation of a further application declaring this same permission, will be prevented.
The alternatives are to reinstall the application requiring the permission, so they are detected by the System, but that is not a good user experience. Or check at runtime for the permissions of the calling application, but that is not without its theoretical flaws.
Problem
As of Android Marshmallow (6.0 - API 23) an application needs to request permission from the user, to use its own custom permission. A declared custom permission is not automatically granted.
This seems peculiar, given that only one application can now declare it.
To replicate
Declare the custom permission and a BroadcastReceiver in the Manifest.
<permission
android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"
android:description="@string/control_description"
android:icon="@mipmap/ic_launcher"
android:label="@string/control_label"
android:protectionLevel="normal or dangerous"/>
<uses-permission
android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"/>
// etc
<receiver
android:name="com.example.app.MyBroadcastReceiver"
android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">
<intent-filter android:priority="999">
<action android:name="com.example.app.REQUEST_RECEIVER"/>
</intent-filter>
</receiver>
From a third-party application, declare that it uses the custom permission in the Manifest (and accept it via a dialog or the settings) and call:
final Intent intent = new Intent("com.example.app.REQUEST_RECEIVER");
context.sendOrderedBroadcast(intent, "com.example.app.permission.CONTROL_EXAMPLE_APP", new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
// getResultCode();
}
}, null, Activity.RESULT_CANCELED, null, null);
The result will return CANCELED and the log will show:
system_process W/BroadcastQueue: Permission Denial: receiving Intent {
act=com.example.app.REQUEST_RECEIVER flg=0x10 (has extras) } to
com.example.app/.MyBroadcastReceiver requires
com.example.app.permission.CONTROL_EXAMPLE_APP due to sender
com.example.thirdparty
If I use the standard ActivityCompat.requestPermissions()
dialog to allow the user to accept the permission, the receiver, as you would expect, works correctly.
Question
Is this expected behaviour? Or have I somehow overlooked something?
It would seem ridiculous to raise a dialog saying
The application Example App wants permission to use Example App
And it may indeed concern the user, providing them with such a nonsensical request.
I can of course change the permission description and name, to something that they would accept, such as 'communicate with other installed applications', but before I sigh and take that approach, I thought I'd ask this question.
Note
The example of the ordered broadcast is to replicate the problem. My application does use other implementations of content providers and a bound service. It is not an alternative implementation I require, it's confirmation of the issue.
Thank you for reading this far.
Edit: To clarify, for other implementations, such as declaring a permmission on a Service (which would be most simple to replicate) the declared custom permission is automatically granted.
As I understood you tried to do next thing (At least, that's how I was able to reproduce your problem):
You declare your new custom permission in first (lets call it F) application
<permission
android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"
android:description="@string/control_description"
android:icon="@mipmap/ic_launcher"
android:label="@string/control_label"
android:protectionLevel="normal or dangerous"/>
You define that your F app uses com.example.app.permission.CONTROL_EXAMPLE_APP
permission. That is right as the guideline says.
<uses-permission
android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"/>
You declare your custom broadcast receiver in your F app. To communicate with that broadcast your app (it's no matter which one, F or other app) must obtain your custom permission
<receiver
android:name="com.example.app.MyBroadcastReceiver"
android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">
<intent-filter android:priority="999">
<action android:name="com.example.app.REQUEST_RECEIVER"/>
</intent-filter>
</receiver>
You define that you second (lets call it S) application uses com.example.app.permission.CONTROL_EXAMPLE_APP
permission. Because you want to allow S app to send broadcast messages to F app receiver.
<uses-permission
android:name="com.example.app.permission.CONTROL_EXAMPLE_APP"/>
Finally you try to send broadcast message from your S app using this code.
final Intent intent = new Intent("com.example.app.REQUEST_RECEIVER");
context.sendOrderedBroadcast(intent, "com.example.app.permission.CONTROL_EXAMPLE_APP", new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
// getResultCode();
}
}, null, Activity.RESULT_CANCELED, null, null);
And, this is important, you granted permission to your S app, but you didn't grant permission to your F app.
As result your broadcast receiver declared in F app didn't receive anything.
After you granted permission to your F app (Note that now S and F granted your custom permission) everything works fine. Broadcast receiver declared in F app received message from S app.
I guess that is correct behaviour, because this doc says us:
Note that, in this example, the DEBIT_ACCT permission is not only
declared with the element, its use is also requested with
the element. You must request its use in order for
other components of the application to launch the protected activity,
even though the protection is imposed by the application itself.
And app which declare permission also must request the same permission to communicate with itself.
As result, android API 23 should to get access to use your permission form user first. And we must to get 2 granted permissions, first from F app (because guidline says as that) and second from S app (because we just need to get access).
But I didn't catch your next point:
It would seem ridiculous to raise a dialog saying
The application Example App wants permission to use Example App
My native Android API 23 displays me something like that:
The application Example App wants
I think the problem in your example is that you explicitly require that both your applications are granted the custom permission.
This part requires, that the com.example.thirdparty app has the permission:
<receiver
android:name="com.example.app.MyBroadcastReceiver"
android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">
And this part requires, that the com.example.app application has the permission as well:
context.sendOrderedBroadcast(intent, "com.example.app.permission.CONTROL_EXAMPLE_APP", ...
You mention that you don't have this problem when using a service. I don't know how exactly you use the service, but if you simply declare it like this:
<service
android:name="com.example.app.MyService"
android:permission="com.example.app.permission.CONTROL_EXAMPLE_APP">
and then bind it like this:
context.bindService(serviceIntent, mServiceConnection, ...
then it's enough if com.example.thirdparty has the permission granted, while com.example.app doesn't need to have it.
In other words, I think this behavior is by design, and the difference you see between the Broadcast and Service behavior is because in the Broadcast case you specifically request that the com.example.app has the custom permission, whereas in the Service case you don't.
I hope I didn't misunderstand your problem. If I did, please let me know and I'll delete this response.
I don't think it's completely true that a declared custom permission will not be automatically granted to the application. For when the custom permission has the protection level "normal" or "signature", then the permission is granted at install time. Otherwise, if the protection level is "dangerous", then it is a runtime permission and it works just like other dangerous permissions: you will need to prompt the user to grant the permission to the application.
While it might be ambiguous for the user to see a permission request for the app which is declared in the same app, it is how android is designed to run since marshmallow. I think, on android's perspective, the behavior is as intended and correct.
First add permissions in manifest file after
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />