AltBeacon / android-beacon-library

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

Is `foregroundScanPeriod` useless below 5 minutes? #1038

Closed Whathecode closed 6 months ago

Whathecode commented 3 years ago

I'm trying to figure out what determines when didExitRegion is called, since the documentation is severely lacking in this regard. For one, the documentation on foreground service seems to explain how you can call setBackgroundScanPeriod and setBackgroundBetweenScanPeriod, which from perusing the codebase seems entirely irrelevant, since when you are in foreground mode the relevant settings are the corresponding foreground settings.

Digging deeper into the code, since changing foregroundScanPeriod seemingly didn't have a meaningful effect in terms of whether didExitRegion got called, or is non-deterministic at the very least, I discovered:

    public int getScanJobRuntimeMillis() {
        long scanPeriodMillis;
        LogManager.d(TAG, "ScanState says background mode for ScanJob is "+getBackgroundMode());
        if (getBackgroundMode()) {
            scanPeriodMillis = getBackgroundScanPeriod();
        }
        else {
            scanPeriodMillis = getForegroundScanPeriod();
        }
        if (!getBackgroundMode()) {
            // if we are in the foreground, we keep the scan job going for the minimum interval
            if (scanPeriodMillis < MIN_SCAN_JOB_INTERVAL_MILLIS) {
                return MIN_SCAN_JOB_INTERVAL_MILLIS;
            }
        }
        return (int) scanPeriodMillis;
    }

So essentially, if you are in foreground mode, any value < 5 minutes (MIN_SCAN_JOB_INTERVAL_MILLIS) is ignored? Could somebody explain me how foregroundScanPeriod and foregroundBetweenScanPeriod impact didExitRegion, if at all?

I also later discovered the static BeaconManager.setRegionExitPeriod() (which is silently ignored if beacon manager is not yet initialized), which seems to be the main relevant setting which influences how long to wait before firing didExitRegion, but that wasn't obvious from the documentation/API at all. I'm still interested in understanding how the scan period settings impact this.

davidgyoung commented 3 years ago

The specific time for region exit or non-deterministic, but is within predictable ranges. It is complex, so I wrote a blog post with a table a few years back which explains why this is so complex: http://www.davidgyoungtech.com/2017/08/07/beacon-detection-with-android-8

Look in the table for the number of seconds to detect a beacon disappearing.

To summarize the limitations:

Separately from scan cycling, BeaconManager.setRegionExitPeriod() decides how long after a beacon is not detected that a region exit event fires. This will always be at the end of a scan cycle, so the cycle times above affect this as well.

Finally, do not be confused by MIN_SCAN_JOB_INTERVAL_MILLIS. This does not affect scan cycles. It just keeps job scheduler runs in the foreground (generally one runs immediately after the other) from being so short that the hurt performance by restarting too often. There is a CycledLeScanner at a lower level that turns scans on and off to match the configured intervals.

@whatthecode I would welcome a PR for improving the documentation on this. But I am not sure the best way to document this given the complexity. I will note that with iOS Core Location (after which this library’s API is modeled for cross-platform purposes) there are similar complexities that affect the timing of region exits and they are totally undocumented! At least with open source you can look at the code and ask the developer questions. :)

Whathecode commented 3 years ago

Thank you for elaborating! 🙏 This clears some things up.

I'm still uncertain about the following:

If the library is configured to use a foreground service for scanning, the above limitation does not apply.

Our primary use case is having a constant foreground service running (we are doing continuous data collection).

The scan periods are otherwise respected, with the background variants used when the app is not visible and BackgroundPowerSaver is active.

Do I understand correctly that this means while the screen is on foregroundScanPeriod/foregroundBetweenScanPeriod is used, and while it is off backgroundScanPeriod/backgroundBetweenScanPeriod is used? Maybe my confusion stemmed from thinking that "foreground service" mapped to "foreground" scan, while this is not the case?

P.s. the documentation does not call updateScanPeriods() after setting them. Is it needed? :) I might contribute PRs if we decide to continue using this library and it would open up more flexibility/use cases. However, I am currently also considering a more low-level library like SweetBlue with which I had success before integrating to other BLE devices.

davidgyoung commented 3 years ago

Yes, foregroundScanPeriod and foregroundBetweenScanPeriod are applied if BackgroundPowerSaver determines an app activity is visible with the screen on. Otherwise backgroundScanPeriod and backgroundBetweenScanPeriod are applied.

Whether or not there is a foreground service is irrelevant to which scan period settings are active.

Generally there is no need to call updateScanPeriods(). The only case where you do is if you want to change these periods after scanning has already started. In this case making the call to updateScanPeriods() will apply the new periods immediately. Otherwise they will be applied at the next foreground/background transition.

Yes, if you want full control over scans, then you probably want to use raw Android APIs. This library is designed to make it easier to handle common beacon use cases.