0

A few years ago, I wrote a tiny application to apply some settings changes to my phone when the charging state changed.

To do this at the time, I registered a receiver in the manifest:

<receiver android:name=".services.PowerStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
        <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
    </intent-filter>
</receiver>

This receiver ran a few lines of code, and that was it. It worked perfectly.

Since API 26, this no longer works. I have to register the receiver at runtime, and it only seems to work while the app is open and for a while after it leaves foreground. I tried to switch methods, then tried using a Service to register the receiver with START_STICKY, to no avail.

How can I achieve the same effect with the current API restrictions?

salezica
  • 74,081
  • 25
  • 105
  • 166

1 Answers1

1

It is my understanding that Google clamped down on anything that allows the app to run in the background over the past few API versions, and there are only a few things that are exempt, and they're all pretty hacky. With any of these, you'll probably want to do the following 2 things:

  1. You'll need to make sure that your app is exempt from battery optimizations. You can either do it manually, or have your app launch an intent that'll let the user do it easily.
  2. You should also set your app Manifest with android:persistent="true", and extend your Appication class to keep it running in the background

And then, here are a few ways that you can have your app "wake up" or stay running:

  • Set up your app to have Notification Access with a NotificationListenerService, and then launch your BroadcastReceiver inside onListenerConnected(). That will keep the service running in the background, and because your receiver was registered inside it, it'll keep that running as well
  • Set up AlarmManager to periodically send your app an intent to wake it up, and then re-launch your BroadcastReceiver
  • Set up Firebase push notifications to periodically wake up your app, and it can then spin up the BroadcastReceiver

I think you can also set up your app as an AccessibilityService, but I don't remember if that will run continuously in the background

Edit: I forgot that you can also use WorkManager to schedule periodic wake-ups, and check if the power is plugged in. Here's how to do it

user496854
  • 6,461
  • 10
  • 47
  • 84
  • Great answer, thanks. I will try these approaches and get back. How about foreground services? How are those treated by the system? Having a permanent notification is hardly ideal, but can it be marked as silent, so it doesn't bother me? If, as a user, I disable a notification channel, can it still be used for a foreground service? If you know, I'd appreciate the info – salezica Sep 02 '22 at 23:50
  • 1
    You can actually use work manager with ongoing work, which will automatically create a foreground notification for you. However, Google specifically states that if that notification is on a silent channel, it won't continue running the service. I don't know what happens if you make it a normal notification channel, but the user chooses to silence it – user496854 Sep 03 '22 at 00:05
  • I've had this running for about a week now on a `NotificationListenerService`, with almost perfect results. The service still seems to die occasionally, but it's rare and I do believe a periodic wake-up with a nicely configured WorkManager task will take care of that. Thanks! – salezica Sep 14 '22 at 12:53