AltBeacon / android-beacon-library

Allows Android apps to interact with BLE beacons
Apache License 2.0
2.84k stars 836 forks source link

Background scanning is not able to immediately detect a beacon #1045

Closed gpiancastelli closed 3 years ago

gpiancastelli commented 3 years ago

Expected behavior

When the app starts the first time, creates RegionBootstrap objects, and one of the beacons for the monitored regions is near the device, the scanning by jobs in background should immediately detect the beacon.

Actual behavior

The beacon is detected only after a 15 minutes interval, as if the first job is not immediately run, but delayed as any other background job would be.

Steps to reproduce this behavior

See the Expected behavior.

Mobile device model and OS version

Xiaomi Mi A3 Android 11

Android Beacon Library version

2.17.1 and 2.18. Please note that with 2.17 the beacon is indeed immediately detected.

IMPORTANT: This forum is reserved for feature requests or reproducible bugs with the library itself. If you need help with using the library with your project, please open a new question on StackOverflow.com.

gpiancastelli commented 3 years ago

It's hard to understand what's going on because the log for 2.17 shows "Running immediate scan job" roughly every five minutes, but this line is totally absent for 2.17.1 and 2.18. It's like 2.17 continually scans, renewing the job every five minutes. But I don't see any indication of actual scanning results.

However, versions 2.17.1 and 2.18 show log lines for the backgound jobs every 15 minutes or so, indicating that the library actually knows about being inside a region, and seem not to engage with Bluetooth otherwise.

Ideally, I guess I would like an "immediate scan job" to start, well, immediately, then after it stops only "regular" scan jobs to run at the time interval allowed by the operating system.

Has this anything to do with the "Improve HW filter detection mechanism in order to detect beacons immediately after starting in background (#974, Vlad Vladau)" item I see in the CHANGELOG for 2.17.1?

davidgyoung commented 3 years ago

@gpiancastelli it appears the library change that caused this was #975. It appears this change was inadvertently left out of the CHANGELOG

I suspect it may be some way your are using the library that is causing this behavior in your case. If the library is in "foreground mode" it should not behave how you describe. Does your app call beaconManager.setBackgroundMode(false); or use BackgroundPowerSaver (which would do the same upon app launch)?

I wonder if this change introduced a problem where failing to do one of the above causes this issue.

gpiancastelli commented 3 years ago

I can confirm that, if I call beaconManager.setBackgroundMode(false); before starting to create RegionBootstrap objects, I witness on 2.18 the same behaviour of 2.17: immediate scan job starting and renewing every five minutes.

However, I am now a little confused about it. First and foremost, this 5-minutes scan job renewal seems to be going on regardless of the application (really, the main/only activity) being in background or in foreground. I have always understood that Android would force limits on how often (and how long) background jobs can be run, and that the 15 minutes I previously saw on 2.18 were the minimum threshold which is actually impossible to go below to. And, if you wanted stuff to run coninuously, you ought to let the user know by starting a foreground service (which is forced to display a notification as long as it's running). This whole "Scan Job runtime expired" / "Running immediate scan job" dance that I see the library doing right now with the application in background seems to counter my assumption on how Android currently (well, at least from version 8) works. How is it possible? Is this still guaranteed to resume in 25 minutes if I kill the app? And is it guaranteed to keep working like this, or is it something I'm doing (e.g. debugging on the wire) that allows the library to get away with it suffering no penalties by the operating system?

If I use BackgoundPowerSaver instead, nothing changes w.r.t. my original issue report. The application starts, but the beacon is not immediately detected. No immediate scan job starts. After 10-15 minutes a periodic scan job is run, and I notice an error message ("Failed attempting to start service: com.example.myapp/org.altbeacon.beacon.BeaconIntentProcessor") that I have never seen before. I think the error is why, even if I see that "we are inside a beacon region" from logging, my app does not receive the callback that would notify it of being inside the region. However, now the library seems to react properly when the application is put in different states: e.g. when resumed in the foreground an immediate scan job is run; when the phone screen goes off the job is stopped; with the screen off, only periodic jobs are run. (Despite the error above, my application is correctly notified of being inside a beacon region when resumed and the immediate scan job kicks in.)

So I guess BackgroundPowerSaver is what I want, except for the beacon which is not immediately detected, and the error preventing my application to be notified on the first periodic job run. How should I proceed to work around those issues?

davidgyoung commented 3 years ago

This is not expected:

If I use BackgoundPowerSaver instead, nothing changes w.r.t. my original issue report. The application starts, but the beacon is not immediately detected. No immediate scan job starts.

I believe this is something distinct to your app setup. It is as if BackgroundPowerSaver thinks your app is in the background (it has no visible activity). Could this be true that it has no visible activity? If you try the library reference app I am confident you will find that you do not see this issue. What is the difference with your app?

The behavior you describe in the quote above is expected background mode behavior. The constant immediate scan job behavior you described in your last comment is expected foreground mode behavior. (Foreground mode is the default mode if not using BackgroundPowerSaver). BackgroundPowerSaver just tracks visible activities to switch library mode automatically.

BTW because of the common confusion here library version 2.19 deprecates direct use of any of these methods and automatically creates BackgroundPowerSaver using the non-deprecated start monitoring/ranging methods.

gpiancastelli commented 3 years ago

I think I found the culprit of my BackgroundPowerSaver setup: the object was created passing a proper Context and was stored inside an object chain that is ultimately referenced from my own Application class, but it was created after the Activity has been already displayed on screen, so a whole bunch of callbacks (particularly onActivityResumed, I see) were not called. I tried moving the construction of BackgroundPowerSaver in a different place and now I see the proper behaviour.

The reason for my setup is that beacon scanning is an optional part of the application, and, ideally, it should be possible to switch it on/off depending on some setting that the application periodically downloads from the Internet. Therefore, I tried creating the BackgroundPowerSaver instance only when beacon scanning was actually required, and nullifying its reference when it wasn't anymore. Now I see that both these actions would not work: the object must be created before the Activity is, and there is no way to actually unregister it from the activity callbacks. I wonder if a custom BackgroundPowerSaver implementation could solve both problems and if this approach would still be feasible from 2.19 onwards.

davidgyoung commented 3 years ago

Interesting, @gpiancastelli. The problem here is that BackgroundPowerSaver uses onActivityPaused/onActivityResumed events to track whether the app is in the foreground or background.. This works well, but it will not set the initial state correctly, and assumes it is in the foreground when constructed. In your case, the deprecated RegionBootstrap resets the background mode to false when constructed, so constructing it when the app is in the foreground will cause the issue.

This initial state issue will become more of a problem with 2.19+ where BackgroundPowerSaver is created automatically. If an app start ranging/monitoring from the background (implicitly creating a BackgroundPowerSaver at that time), it will incorrectly be in foreground mode. To fix this there needs to be a way to infer whether the app is actually in the foreground at the time we start tracking these events.

davidgyoung commented 3 years ago

The initial background/foreground state for autobind is addressed here #1051. Since the non-autobind methods are now deprecated, the recommended solution is to switch to using autobind methods.