Search code examples
androidbroadcastreceiverandroid-broadcast

Testing that BroadcastReceiver does not receive protected broadcast


I'll try to outline this scenario as well as I can. The main question is: how can I declare 2 receivers dynamically which both receive the same broadcast, but only one has the correct permission to receive it? In my testing so far, both receivers get the broadcast due to my app holding the declared permission, rather than just the one.

In App A, I am sending a broadcast which I am protecting with the new permission I am defining.

In App B, I want to ensure that the broadcast from App A is in fact being protected. So I dynamically declare the 2 receivers for the same IntentFilter, one holding the new permission and one without. However, in App B's manifest, I of course declare <uses-permission android:name="new permission" />

However, both receivers are getting it, not just the one holding the permission. I assume that it's because App B itself is declared to use the permission. Here's my code:

App A manifest:

<permission
    android:name="com.my.custom.permission"
    android:label="my_permission"
    android:protectionLevel="signature" />

App A source:

Intent intent = new Intent(SOME_CUSTOM_ACTION);
...
sendBroadcast(intent, "com.my.custom.permission");

App B manifest:

<uses-permission android:name="com.my.custom.permission" />

App B service:

private BroadcastReceiver rNoPermission = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (SOME_CUSTOM_ACTION.equals(intent.getAction())) {
            Log.d(TAG, "receiver was able to receive without permission");
        }
    }
};

private BroadcastReceiver rYesPermission = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (SOME_CUSTOM_ACTION.equals(intent.getAction())) {
            Log.d(TAG, "receiver was able to receive properly with permission");
        }
    }
};

public void start() {
    IntentFilter filter = new IntentFilter(SOME_CUSTOM_ACTION);
    registerReceiver(rNoPermission, filter); // purposely don't register with permission
    registerReceiver(rYesPermission, filter, "com.my.custom.permission", null);
}

public void end() {
    unregisterReceiver(rNoPermission);
    unregisterReceiver(rYesPermission);
}

Flow:

  1. App A installed for Android to learn new permission
  2. App B installed and run
  3. App B start() method is called
  4. Switch to App A to trigger sending the broadcast
  5. See in logs that both BroadcastReceivers get called
  6. End test by calling end() method

Any ideas?


Solution

  • Realized where my mistake is. Permissions are granted at the application level, not at the component level. As per Android documentation:

    To enforce a permission when sending, you supply a non-null permission argument to sendBroadcast(Intent, String) or sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle). Only receivers who have been granted this permission (by requesting it with the tag in their AndroidManifest.xml) will be able to receive the broadcast.

    (Source: http://developer.android.com/reference/android/content/BroadcastReceiver.html)

    and:

    Senders of an intent can verify that the recipient has a permission specifying a non-Null permission with the method call. Only applications with that permission will receive the intent.

    (Source: http://developer.android.com/training/articles/security-tips.html)

    So as of now there doesn't appear to be a way to do what I mention above without splitting the BroadcastReceivers into 2 separate apps, one using the permission and one without.