Android: Problem with BLE scanning stopped while screen is off on newer phones

Hello,

We're using the Scanner Compat (v1.6.0) library for Android in our app, and it has helped us tremendously.

Recently we've observed that the Xiaomi 12T has stopped delivering scan results with the screen off. This means that users cannot reliably connect to our device anymore, unless the user briefly activates the screen on their phone, in which case the scan results pop in instantly, leading to a prompt successful connection with our device as expected. This hasn't always been a problem, and I can probably trace it back to an Android 14 update.

For context, we're using PendingIntent scan strategy to scan continuously, scan filters for our devices, scan settings of SCAN_MODE_BALANCED, and we're restarting the scan every 15min via a scheduled job. We also have the batteryOptimization disabled, as well as the ACCESS_BACKGROUND_LOCATION permission.

In my investigation of the problem, I stumbled upon the reportDelay scan setting, which we havn't been using before. Re-reading the README page of the Scanner Compat library, there is a small mention of this, saying that it should be used with PendingIntent background scanning. Also, in a repo of Googles samples for working with BLE, I fould a small comment saying something along the lines of "this [reportDelay] enables scanning while the screen off"

Thinking that I've found the solution to our problems, I set up a test with the Xiaomi12T with our previously mentioned scan setup but also a reportDelay of 10000ms.

While testing, inspecting the phone log in LogCat with the filter `tag:BtGatt.ScanManager` shows that scan results - with screen off - are being "flushed" (`MSG_FLUSH_BATCH_RESULTS`) on what seems to be a strict timer of 5 minutes. I interpret this "flushing" as the phone buffering scan results, to be released after 5 minutes *only* to apps having the reportDelay (batching?) enabled.

Together with the reportDelay, when the flushing happens, we CAN actually connect to our devices from background with the screen off - unfortunately with the added inconvenience of up to 5 minutes delay from the assumed phone built-in timer.

This wouldn't be so bad if it was isolated to just one phone, but our users report what seems to be the same behavior on newer OnePlus models, running Android 15. I am hesitant to go ahead and set this reportDelay for all of our users across the board, as I have no idea how other phones react. At the very least, users will see an increased delay in bluetooth conection time, which isn't exactly an improvement.

Do you have any thoughts about or insights into this issue? Like, is this the expected behavior for newer phones? Is there some combination of scan settings, phone settings, or other settings that will mitigate this behavior?

Kind regards,

Robin

Parents Reply Children
  • Hello

    Absolutely! But let me be clear, our setup has worked - and is working - as expected on many phones that we support. What we're seeing lately, however, is a worrying trend where that this setup doesn't work anymore on certain phones, including, but presumably not limited to, the Xiaomi12T we use for testing. More precisely, we've heard complaints from Xiaomi and OnePlus customers, experiencing the same behavior: Not connecting automatically anymore with the screen off. Briefly activating the screen, however, triggers the expected instant connection.

    As mentioned earlier, it seems non-batched scans with PendingIntents are being suppressed with the screen off, and even performing batched scanning via the reportDelay scan setting puts the scan results on a fixed 5 min timer. This might be fine for a smart watch that periodically syncs throughout the day, but not for our devices where an instant automatic connection is part of the core functionality.

    Here's the filter definition:

    private val SCAN_FILTER = listOf(
    ScanFilter.Builder()
    .setServiceUuid(ParcelUuid.fromString(<service UUID 1>)
    .build(),
    ScanFilter.Builder()
    .setServiceUuid(ParcelUuid.fromString(<service UUID 2>)
    .build(),
    ScanFilter.Builder()
    .setServiceUuid(ParcelUuid.fromString(<service UUID 3>)
    .build())

    and the use site:

    override fun startScan() {
    BluetoothLeScannerCompat.getScanner().startScan(SCAN_FILTER, SCAN_SETTINGS, context,
    scanningPendingIntent, RC_START_STOP_SCANNING)
    }
Related