20

I've read about Android Oreo background execution limitations, and it clearly states that BOOT_COMPLETED broadcast is unaffected, but I can't get it to work on Android Oreo.

First, I am compiling against SDK 27. Secondly, I declared the receiver inside the manifest file:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <receiver
        android:name="helpers.StartDetectionAtBoot"
        android:label="StartDetectionAtBoot"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <category android:name="android.intent.category.DEFAULT"/>

            <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>

            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="android.intent.action.QUICKBOOT_POWERON"/>
            <!--For HTC devices-->
            <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
            <!--For MIUI devices-->
            <action android:name="android.intent.action.REBOOT"/>
        </intent-filter>
    </receiver>

Then there's the implementation of the receiver, which can also be simple as that:

public class StartDetectionAtBoot extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("test", "test");

        Intent intent0 = new Intent( context, ActivityRecognitionService.class );
        PendingIntent pendingIntent = PendingIntent.getService(context, 111, intent0, PendingIntent.FLAG_UPDATE_CURRENT);
        ActivityRecognitionClient activityRecognitionClient = ActivityRecognition.getClient(context);
        activityRecognitionClient.requestActivityUpdates(5000, pendingIntent);
    }
}

onReceive method is not called and I will always get logcat error on Android Oreo devices/emulator:

W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.BOOT_COMPLETED flg=0x400010 }

Reading other answers, they said there were some problems when registering explicit intents in the manifest but this is not the case of BOOT_COMPLETED one.

Neither this helped because the receiver is not called at all.

Registering broadcast intent at runtime, get it to work (on the emulator, firing the intent from adb shell), but I'm not sure it's the right way to do it:

registerReceiver(new StartDetectionAtBoot(), new IntentFilter(Intent.ACTION_BOOT_COMPLETED));

Are there any known bugs with this?

fillobotto
  • 3,698
  • 5
  • 34
  • 58
  • 1
    What error/behavior do you see, when you start a foreground service? – Sagar Apr 26 '18 at 11:34
  • @Sagar I can start it correctly when app is running and the service stays active even closing the app, but onReceive method of broadcast receiver is never fired otherwise – fillobotto Apr 26 '18 at 11:49
  • 1
    ACTION_BOOT_COMPLETED will be only fired if you restart the phone. And you have to register it in Manifest. Instead of starting IntentService from BroadcastReceiver, start the foreground service and then register for activity client – Sagar Apr 26 '18 at 12:24
  • @Sagar I have already added it to manifest but as I said it's not fired at all when booting the device – fillobotto Apr 26 '18 at 12:53
  • Looking at your error, looks like your boot receiver is working. Just remove the code for ActivityRecognition and check if the log gets printed. – Sagar Apr 26 '18 at 12:55
  • @Sagar this is the point. Even a simple log call inside onReceive will never be called – fillobotto Apr 26 '18 at 12:57

3 Answers3

10

Self-registering your receiver(s) in a code for the needed implicit intents is really the right way to start receiving that intents. No service is needed for this (in most cases, see below...). But you need to be aware of the following in order not to be confused during testing and not to break earlier implementation:

  1. Self-registering should be done once per the application run. In terms of Java / Kotlin application data: once per a static field life. So one static boolean field should allow you to know: if you need to self-register (e.g. after reboot or after Android system killed your app later...) or not (I cite working code from this commit: https://github.com/andstatus/todoagenda/commit/74ffc1495f2c4bebe5c43aab13389ea0ea821fde ):
    private static volatile boolean receiversRegistered = false;

    private static void registerReceivers(Context contextIn) {
        if (receiversRegistered) return;

        Context context = contextIn.getApplicationContext();
        EnvironmentChangedReceiver receiver = new EnvironmentChangedReceiver();

        IntentFilter providerChanged = new IntentFilter();
        providerChanged.addAction("android.intent.action.PROVIDER_CHANGED");
        providerChanged.addDataScheme("content");
        providerChanged.addDataAuthority("com.android.calendar", null);
        context.registerReceiver(receiver, providerChanged);

        IntentFilter userPresent = new IntentFilter();
        userPresent.addAction("android.intent.action.USER_PRESENT");
        context.registerReceiver(receiver, userPresent);

        Log.i(EventAppWidgetProvider.class.getName(), "Registered receivers from " + contextIn.getClass().getName());
        receiversRegistered = true;
    }
  1. Insert the call of your registerReceivers method to all possible entry points of your application in order to maximize chances that the receivers are registered even if your application was started by Android system only once, e.g.:
    @Override
    public void onUpdate(Context baseContext, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        registerReceivers(baseContext);

        ...
    }
  1. You may leave your receivers being registered to the same intents in AndroidManifest.xml file (as this works for Android before v.7 ...), but note that in this case in logcat you will still see "Background execution not allowed" with a reference to your receivers. This only means that registration via the AndroidManifest.xml doesn't work (as expected for Android 8+) but self-registered receivers should be called anyway!

  2. As I noted above, starting foreground service is generally not needed for a light widget. Moreover, a User won't like constantly seeing notification that your "widget" is running in the foreground (and thus constantly eating resources). The only case, when this may be really needed, is when Android kills your application too often and thus deletes that self-registration done after reboot. I think that making your "widget application" as light as possible (requiring as few memory and CPU resources, as possible...) is the right way to ensure that your widget app will be killed only in critical cases for your device... Maybe you should split your large app in two, making a widget sort of a launcher for the heavyweight app that needs to work from time to time...

yvolk
  • 2,391
  • 3
  • 21
  • 26
  • 1
    ~"**Self-registering your receiver**". You mean as opposed to `AndroidManifest`? – IgorGanapolsky Mar 17 '19 at 19:03
  • 1
    @IgorGanapolsky Yes, self-registering receivers, because receivers, defined in the Android Manifest, are skipped/disabled by the Android system. – yvolk Mar 31 '19 at 14:28
  • @yvolk does this mean that if application is killed after self register, we will stop receiving broadcast notification? This is contrary to registering to a broadcast event in manifest. – Kaps May 02 '20 at 05:50
  • @Kaps Yes, my experience showed this. So the app needs a chance to re-register as early as possible... This is why in my widget's code I start processing of any input from checking, if the app is registered for notifications. And register, if not yet. – yvolk May 14 '20 at 06:51
5

W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.BOOT_COMPLETED **flg=0x400010** }

You test the intent from adb shell, may be am broadcast -a android.intent.action.BOOT_COMPLETED The flg=0x400010 is different with flg=0x9000010 which the Android system sent during boot.

You could find the flag definition at frameworks/base/core/java/android/content/Intent.java. The flg=0x400010 does not has the bit FLAG_RECEIVER_FOREGROUND = 0x10000000.

So, if you want to test the intent from adb shell, you could use am broadcast -a android.intent.action.BOOT_COMPLETED *--receiver-include-background.*

Daniil T.
  • 1,145
  • 2
  • 13
  • 33
Xiufeng Deng
  • 51
  • 1
  • 2
3

The solution was a combination of two attempts I had already made.

First, I had to start a foreground service (even a dummy service would be good) with sticky notification:

public class StartDetectionAtBoot extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Intent intent1 = new Intent(context.getApplicationContext(), DummyService.class);
            context.startForegroundService(intent1);
        }

        Intent intent0 = new Intent( context, ActivityRecognitionService.class );
        PendingIntent pendingIntent = PendingIntent.getService(context, 111, intent0, PendingIntent.FLAG_UPDATE_CURRENT);
        ActivityRecognitionClient activityRecognitionClient = ActivityRecognition.getClient(context);
        activityRecognitionClient.requestActivityUpdates(5000, pendingIntent);
    }
}

Of course inside the service you start there must be an onCreate method which creates a notification and calls startForeground.

Secondly, I had did a cache invalidation in Android Studio and I also wiped emulator instance. This part of the solution was necessary for me since the first part still didn't work.

fillobotto
  • 3,698
  • 5
  • 34
  • 58
  • Why did you use two services and not implemented both the foreground service and the activity recognition in one service? – dor506 Jun 24 '18 at 09:37
  • @dor506 no, same service – fillobotto Jun 25 '18 at 14:45
  • 1
    @fillobotto I have similar situation. Can you please paste the contents of DummyService.class. [1]: https://stackoverflow.com/questions/53578252/widget-issue-broadcastqueue-background-execution-not-allowed-receiving-intent – Biswajit Das Dec 02 '18 at 12:50
  • invalidating cache and wiping emulator data worked for my case. I didn't have to use the dummy service, nevertheless, Thank you – user -1 May 03 '20 at 23:49