12

I have a custom button in a sticky notification.
I used to attach a PendingIntent to it for receiving button clicks:

Intent intent = new Intent();

intent.setAction("com.example.app.intent.action.BUTTON_CLICK");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 2000, intent, PendingIntent.FLAG_UPDATE_CURRENT);
contentViewExpanded.setOnClickPendingIntent(R.id.button, pendingIntent);

When i run this code on Oreo , i get BroadcastQueue: Background execution not allowed in logcat and don't receive button click.

I registered receiver with manifest:

<receiver
    android:name=".BroadcastReceiver.NotificationActionReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.app.intent.action.BUTTON_CLICK"/>
    </intent-filter>
</receiver>

I also tried registering receiver in my code:

NotificationActionReceiver mMyBroadcastReceiver = new NotificationActionReceiver();
IntentFilter filter = new IntentFilter("com.example.app.intent.action.BUTTON_CLICK");
mContext.registerReceiver(mMyBroadcastReceiver, filter);

This works but only when the app is visible to user.

Thanks for help

Behnam Maboudi
  • 655
  • 5
  • 21

2 Answers2

29

Never use an implicit Intent when an explicit Intent will work.

Replace:

Intent intent = new Intent();

intent.setAction("com.example.app.intent.action.BUTTON_CLICK");

with:

Intent intent = new Intent(this, NotificationActionReceiver.class);

And remove the <intent-filter> from the NotificationActionReceiver <receiver> element.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • It worked! Thanks. Actually i thought my intent is already explicit because it was targeting my own app. – Behnam Maboudi Sep 02 '17 at 17:48
  • 4
    @BehnamMabodi: An explicit `Intent` is one where the `Intent` knows a specific component that is the target. Typically, you make one via the constructor that takes the Java `Class` object (in this case, `NotificationActionReceiver.class`). An implicit `Intent` is one that lacks the component, and so Android relies on other things like action strings, to find the component for you. – CommonsWare Sep 02 '17 at 18:00
8

I ran into this issue as well on Android 8 - Oreo, but given my library project requirements, I don't have the explicitly named BroadcastReceiver class implementation, that the end-client will declare in it's AndroidManifest.

Solution:

Specify the application package on the Intent using setPackage(String).

Example:

// Application unique intent action String
final String receiverAction = getApplicationContext().getPackageName() 
                             + BaseLibraryReceiver.ACTION_SUFFIX;
// No need for Class definition in the constructor.
Intent intent = new Intent(); 
// Set the unique action.
intent.setAction(receiverAction);
// Set the application package name on the Intent, so only the application
// will have this Intent broadcasted, thus making it “explicit" and secure.
intent.setPackage(getApplicationContext().getPackageName());
...

From the Android Broadcasts: Security considerations and best practices docs.

In Android 4.0 and higher, you can specify a package with setPackage(String) when sending a broadcast. The system restricts the broadcast to the set of apps that match the package.

Here’s an example of the BroadcastReceiver declared (or merged) in to the end-client application’s AndroidManifest:

 <receiver
        android:name=“com.subclassed.receiver.ReceiverExtendedFromLibrary"
        android:exported="false"
        android:enabled="true">

        <intent-filter>
            <action android:name="${applicationId}.action.MY_UNIQUE_ACTION"/>
        </intent-filter>

 </receiver>

Since my example revolves around a library project that broadcasts an Intent, I’ve decided to keep the <intent-filter> and <action /> in the <receiver> declaration. Otherwise, there would be non-unique broadcast actions being fired, which could lead to potential issues where multiple applications receive the wrong broadcast. This is mostly a safety precaution. Of course you still need to check the action in the implementation of the BroadcastReceiver.

Hope someone finds this helpful!

Sakiboy
  • 7,252
  • 7
  • 52
  • 69
  • 1
    EXCELLENT WORK!! I was literally trying to figure out how to do this with dynamic packages, and stumbled across your post. Works like a charm thanks!! – Sam May 16 '18 at 20:30