44

I try to enable and disable a broadcast receiver by using this PackageManager method:

setComponentEnabledSetting(componentName,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
        PackageManager.DONT_KILL_APP);

The broadcast receiver is registered in the manifest. The receiver works fine but when i try to disable it, it still receives the broadcast messages. When i disable the receiver in the manifest by "android:enabled="false"", the receiver does not receive anything but I can not enable it.

I call the method from inside a service.

    PackageManager pm  = getApplicationContext().getPackageManager();
    ComponentName componentName = new ComponentName("com.app",
             ".broadcast_receivers.OnNetworkChangedReceiver");
    pm.setComponentEnabledSetting(componentName,
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP);

Android manifest:

    <receiver android:name=".broadcast_receivers.OnNetworkChangedReceiver"
                android:enabled="true">
            <intent-filter>
                    <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
            </intent-filter>
    </receiver>

The Receiver

public class OnNetworkChangedReceiver extends BroadcastReceiver {
private static final String TAG = "OnNetworkChangedReceiver";

@Override
public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "in OnNetworkChanged");
    }
}

I also called the method from inside an Activity yesterday. I thought it worked but today nothing works anymore. Could it be that there is sometimes a big delay in the intent (android.net.conn.CONNECTIVITY_CHANGE) that I misinterpreted yesterday as disabling the receiver?

Is the approach with the PackageManager the right direction or is there a basic error in the idea?

Thanks a lot, Sven

Sven
  • 1,648
  • 1
  • 21
  • 31
  • By the way @Sven, your initial mistake was a very common one. Your ComponentName needed ("com.app", "com.app.broadcast_receivers.OnNetworkChangedReceiver"); I realize this solution looks weird, because the package name seems to be listed twice. And since the 'New Android Wizard...' may only ask you for only one package name when you first create a project, it's easy to think there is only one, but under the covers it actually takes that string you give it and assigns to two different package names, both the Application package name and also the Activity package name. – Stephan Branczyk Dec 21 '11 at 02:48
  • Using `0` instead of `PackageManager.DONT_KILL_APP` could also help you get rid of the activity, if I'm reading the docs correctly: http://developer.android.com/reference/android/content/pm/PackageManager.html in the `SetComponentEnabledSetting` section. – Ehtesh Choudhury May 07 '13 at 01:48

2 Answers2

74

Well, what you basically have seems OK. I have the following code in one of my projects:

boolean enabled=prefs.getBoolean(key, false);
int flag=(enabled ?
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
ComponentName component=new ComponentName(EditPreferences.this, OnBootReceiver.class);

getPackageManager()
    .setComponentEnabledSetting(component, flag,
                                PackageManager.DONT_KILL_APP);

I use this to disable a BOOT_COMPLETED receiver when I don't have any active alarms that need to be scheduled.

My guess is that your ComponentName is not set up properly (e.g., your leading .). Try the constructor that I am using, that takes a Context and a Class as parameters.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    Thanks a lot for the fast answer. I used the constructor you used and now it works. – Sven Apr 11 '11 at 18:44
  • Say I have a Boot Receiver - would it be any worth disabling it just before its `onReceive` returns ? Would it then run on next reboot ? – Mr_and_Mrs_D Mar 18 '13 at 00:29
  • 1
    @Mr_and_Mrs_D: If it is disabled when the device reboots, it will not be invoked. – CommonsWare Mar 18 '13 at 11:21
  • Thank you I found out :( I thought the changes would not persist - how come they do ? Does `setComponentEnabledSetting` write in the manifest ? – Mr_and_Mrs_D Mar 18 '13 at 14:04
  • @Mr_and_Mrs_D: "I thought the changes would not persist" -- then there would have been no point in your trying to disable the receiver, as the boot would already have happened. "how come they do ?" -- because that is the complete and entire point behind `setComponentEnabledSetting()`. – CommonsWare Mar 18 '13 at 14:17
  • "then there would have been no point in your trying to disable the receiver, as the boot would already have happened" - _would it not remove some overhead_ ? - Since the receiver will be matched against all implicit intents broadcasted while it is registered [it won't actually respond (its onReceive() wont run) as I test for the action (to avoid malicious explicit intents) and I have the BOOT_COMPLETED filter active - so implicit intents won't match, apart from the BOOT one] – Mr_and_Mrs_D Mar 18 '13 at 14:33
  • 1
    @Mr_and_Mrs_D: "would it not remove some overhead ?" -- most likely you are causing *more* overhead by your call, as it involves inter-process communication. "Since the receiver will be matched against all implicit intents broadcasted while it is registered" -- since most of the Googlers working on Android are talented developers, I would imagine that they use advanced data structures like `HashMap` to make these lookups efficient. Moreover, since you cannot determine whether your change actually improves performance, you are wasting your time. – CommonsWare Mar 18 '13 at 14:40
  • Major caveat: be aware that enabling or disabling a component with DONT_KILL_APP on API 14 or 15 will wipe out any notifications your app has created. See https://code.google.com/p/android/issues/detail?id=21635 – Andy Dennie Nov 13 '14 at 19:05
  • clarification: my previous comment applies to "ongoing" notifications. – Andy Dennie Nov 13 '14 at 19:14
  • @CommonsWare Please, i dont understand clearly what the flag DONT_KILL_APP is useful for. Is not the Receiver's onReceive() already guaranteed to be executed completely (with limit of 10 secs)? – GPack Oct 22 '16 at 08:38
  • @GPack: It is a flag for `setComponentEnabledSetting()` that controls whether the process should be terminated when we are toggling whether a component is enabled or not. It has nothing directly to do with any `onReceive()` method. – CommonsWare Oct 22 '16 at 11:05
3

I think using the PackageManager is over-thinking your situation. You have a BroadcastReceiver that needs to sometimes ignore the broadcasts it's listening for. I can think of two easy ways to go about this:

1) Set a flag that your receiver can check to ignore or accept broadcasts, and don't worry about enabling/disabling it at all.

2) Create the BroadcastReceiver programmatically (can just be an inner class, even), and register and unregister it as you need at given parts of your application.

In general I've found that defining my BroadcastReceivers in code instead of XML has provided a lot more flexibility and is generally easier for me to manage.

LeffelMania
  • 12,765
  • 4
  • 32
  • 34
  • 10
    There are some types of broadcasts (e.g., `BOOT_COMPLETED`) that cannot be effectively registered via `registerReceiver()`. Also, over-reliance on `registerReceiver()` leads you to everlasting services -- services just there to maintain a receiver -- which is bad. Disabling the component is a good idea for efficiency, particularly with popular system broadcasts, like `BOOT_COMPLETED`. – CommonsWare Apr 11 '11 at 17:37
  • 2
    Very good points. Programmatic BroadcastReceivers do necessitate responsible registering/unregistering to avoid everlasting services. As always it depends on the situation. – LeffelMania Apr 11 '11 at 17:54
  • @CommonsWare Is there a way to only enable the BroadCastReceiver after a BOOT_COMPLETED event? I would like to enable the Receiver immediately after the device re-boots, re-set alarms from my SQLite database that fire due date Notifications and then disable the BroadCastReceiver. Can I disable the Receiver if the alarms are set up with PendingIntents or do I need to always leave enabled (rendering the first question moot)? – AJW Jul 11 '18 at 22:52
  • 1
    @AJW: "Can I disable the Receiver if the alarms are set up with PendingIntents" -- if those `PendingIntents` will be for broadcasts to the receiver, the receiver must be enabled to receive those broadcasts. That receiver does not need to be *exported*, but it does need to be *enabled*. – CommonsWare Jul 11 '18 at 23:06