Closed zahichemaly closed 2 years ago
I believe your analysis of the issue is correct, @zahichemaly. Unfortunately, Android's evolving rules about foreground services don't make this easy.
Whether or not the library starts up with a foreground service is configurable. A few strategies with the exiting library:
beaconManager.setEnableScheduledScanJobs(false)
. This will use a regular background service for scanning subject to background rules, but if you app also has another foreground service, that scanning service will get all the benefits of the foreground service.6. I realize all this is complex. I am open to ideas on improving the library to make its behavior more intuitive, but I'm not sure what that would be. We could change the library to refuse to start up the foreground service (if so configured) until the app is in the foreground. But I worry this change would be unexpected and surprising to novice users and may cause unintended side effects. I am not sure this is better than letting the exception happen, and four months from now Android might change the rules all over again!
Thanks @davidgyoung for the detailed explanation. I will look into those alternatives. Regarding the foreground service scanning on Android12+, the user is currently aware that the app is crashing through an "app not responding" popup, and also in the app manager which details the issue. The only workaround so far is changing the battery setting of the app to "Unrestricted", which is not intuitive. So for the meantime, I suggest to catch the exception being thrown and log it in the library just to improve the user experience. This of course only applies to Android 12+. What do you think?
I am a little worried that catching the exception will mask the problem and hide it from users. You found this problem because you saw the exception, right?
Here's an alternative proposal:
beaconManager.enableForegroundServiceScanning
and add a deprecation reason explaining the Android 12 problem. The deprecation make make this more visible.beaconManager.enableForegroundServiceScanningWhenPossible
. It would behave just like the existing method, except if a foreground service could not be started on Android 12, it would log a warning and fall back to using the default job scheduler mechanism of scheduling scans. The restrictions on foreground services would would be documented in the method.One other idea…
if when preparing to start the foreground service, the library could check three conditions:
If all three of the above are true, the app could refuse to start the foreground service to prevent a crash. Instead it could:
If a detection comes in via intent the flag above could be checked and the foreground service started at that time.
I am experimenting with an implementation similar to my last comment, but using ScanJob as the fallback strategy. So it would work this way:
My first attempt at this is not working yet. Not sure why:
05-22 19:09:15.811 14753 14753 D StartupBroadcastReceiver: onReceive called in startup broadcast receiver
05-22 19:09:15.816 14753 14753 D StartupBroadcastReceiver: Passive background scan callback type: 1
05-22 19:09:15.816 14753 14753 D StartupBroadcastReceiver: got Android background scan via intent
05-22 19:09:15.816 14753 14753 I BeaconManager: unbinding all consumers for failover from intent strategy
05-22 19:09:15.817 14753 14753 D BeaconManager: Unbinding
05-22 19:09:15.817 14753 14753 D BeaconManager: Not unbinding from scanning service as we are using scan jobs.
05-22 19:09:15.817 14753 14753 D BeaconManager: Before unbind, consumer count is 1
05-22 19:09:15.818 14753 14753 D BeaconManager: After unbind, consumer count is 0
05-22 19:09:15.818 14753 14753 I BeaconManager: Cancelling scheduled jobs after unbind of last consumer.
05-22 19:09:15.822 14753 14753 I ScanJob : Using immediateScanJobId from manifest: 208352939
05-22 19:09:15.824 14753 14753 I ScanJob : Using periodicScanJobId from manifest: 208352940
05-22 19:09:15.826 14753 14753 I BeaconManager: binding all consumers for failover from intent strategy
05-22 19:09:15.827 14753 14753 D BeaconManager: Need to rebind for switch to foreground service
05-22 19:09:15.827 14753 14753 D BeaconManager: Binding to service
05-22 19:09:15.827 14753 14753 I BeaconManager: Attempting to starting foreground beacon scanning service.
05-22 19:09:15.834 14753 14753 W BeaconManager: Foreground service blocked by Android Security Exception. Falling back to job scheduler
Based on my test above, it appears that getting bluetooth scan results delivered by Intent is not sufficient to allow an app to start a foreground service.
The documentation here suggests that starting a foreground service should be allowed in the case that:
Your app receives a Bluetooth broadcast that requires the BLUETOOTH_CONNECT or BLUETOOTH_SCAN permissions
Tests show that delivery of BLUETOOTH_SCAN Intents do not cause this to be allowed. I dug through the AOSP source code, and it seems that bluetooth connection events are rally the only events that do cause this to be allowed.
This means that it is impossible for the library to auto-restart the foreground service at a later time (unless the app comes to the foreground), because the library itself handles no background events that make it eligible. As a result, the best the library could do is:
This is fixed in #1088
As of #1088 and the latest beta version of this library, if you configure a foreground service, the library will try to use it, but if Android 12 blocks it, it caches the Exception and falls back to using the Job Scheduler. If the app moves to the foreground, the library will automatically switch back to using a foreground service (because it is allowed to do so at that time.)
I also added a way to check if the foreground service start failed, and retry starting it if the app knows that a motion, geofence or other event happened that would temporarily cause the operating system to allow a foreground service start.
Details on how this works are here: https://altbeacon.github.io/android-beacon-library/foreground-service.html
I am testing using the latest BeaconReferenceApplication, with foreground service being enabled.
Expected behavior
Foreground service should restart after we stop monitoring for a while.
Actual behavior
App throws
ForegroundServiceStartNotAllowedException
when in the background, and foreground service fails to restart.Logs:
Steps to reproduce this behavior
targetSdkVersion
andcompileSdkVersion
31 or aboveMobile device model and OS version
Galaxy S21 FE 5G (SM-G990E) Android 12
Things I noticed:
disableForegroundServiceScanning
, but the service keeps restarting.