Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling of onScannerRegistered() status=6 (Scanning too frequently) #18

Closed
Rawa opened this issue Jul 3, 2018 · 11 comments
Closed

Handling of onScannerRegistered() status=6 (Scanning too frequently) #18

Rawa opened this issue Jul 3, 2018 · 11 comments

Comments

@Rawa
Copy link

Rawa commented Jul 3, 2018

Hello,

If a scan is started too often it will fail silently, only printing a onScannerRegistered where you have to read the status code. Is there a way to catch this even? When a scan fails because user or app initiated the scan too often? The ScanCallback does not yeild a onScanFailed.

In my app a scan happens directly when a user enters a certain view, thus toggling in and out of the app (resume/pause) several times will cause the scan to start/stop several times. Sometimes the scan fails to start and thus user is unaware that the scan is not actually running because it fails silently.

The same issue can be found in the nRF Connect app and can be easily reproduced there, happily button mashing the Scan button will cause the app to show a state of scanning (button showing "STOP SCANNING") even though the starting of the scan failed.

I kept getting source code not matching byte code when trying to trace down the error so it was hard for me to research this issue further. Tried looking through the Android Source code but it seems like they are not propagating this information to layers above:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/bluetooth/le/BluetoothLeScanner.java#394

Is there any way to know if you are scanning to often and let the user know or just delay the scan until the module is ready?

Best regards
David

@philips77
Copy link
Member

Hmm.. I think you'd have to count it in your app. As you wrote, the callback isn't called and as I can imagine, it depends on the Android version and phone manufacturer, because why not.

@Rawa Rawa changed the title Handling of onScannerRegistered() status=6 (Scanning to frequently) Handling of onScannerRegistered() status=6 (Scanning too frequently) Jul 3, 2018
@Rawa
Copy link
Author

Rawa commented Jul 3, 2018

@philips77 Sure, I could check if i performed a scan within the last 2 seconds and if that is the case then back off and wait for 2 seconds to go by. But how will i know that is enough? Maybe some manufacturer requires 30 seconds?

@philips77
Copy link
Member

True. So the best idea is to assume that users aren't stupid and aren't changing screens over and over to kill the scanner, or design the app so it's now possible. I don't think anything else could be done, actually. You may also check if you aren't getting any records for some time and write preform some action based on this.
As you said, I'm able to reproduce this issue in nRF Connect, where I'm using the standard API.

@budius
Copy link

budius commented Jul 4, 2018

The correct checking for this "no more than 5 start/stop in 30 seconds"
The source code for this start/stop blocking is here: https://android-review.googlesource.com/c/platform/packages/apps/Bluetooth/+/215844/15/src/com/android/bluetooth/gatt/AppScanStats.java#144

This behavior was documented on developer.android.com when Nougat was in developer preview 4, but since has been removed and didn't make to the official documentation (https://developer.android.com/about/versions/nougat/)

For now there's still a copy of that page on the web archive: https://web.archive.org/web/20160820074825/https://developer.android.com/preview/support.html#dp4

We’ve changed the BLE Scanning behavior starting in DP4. We’ll prevent applications from starting and stopping scans more than 5 times in 30 seconds. For long running scans, we’ll convert them into opportunistic scans.

To fix this on our app, our scanner is a singleton and we wrapped in a NougatScanner that delayed the stop of the scan, and if the app starts again within those seconds, there's no need for calling start.

I wonder if that is something that Nordic would be interest in implementing on their library, or they rather leave it as a "thin" compat layer.

I hope it helps.

@Rawa
Copy link
Author

Rawa commented Jul 4, 2018

@budius Nice find. Too bad this is just emitted as an error code when trying to start a scan.

I'll leave this issue open for Nordic to decide what they want to do with it. If this is something you want to add to the CompatScanner library or not.

@philips77
Copy link
Member

I'll close the issue here, but marked it with a label and will mentioned it in the Readme. Thanks @budius for all the information.

@bobatsar
Copy link

Hey @philips77 , I just stumbled about this issue. It seems onScanFailed() is not called, only the following is printed.

onScannerRegistered() - status=6 scannerId=-1 mScannerId=0

Shouldn't the onScanFailed() callback be triggered in this case?

@philips77
Copy link
Member

Well... the lib calls it when the native API is called. But in fact the native onScanFailed() is not being called.

@popy2k14
Copy link

Thanks for this discussion. This issue was driving me nuts until i found the logcat entry "onScannerRegistered() status=6 ...".
Just want to say, sme issue here. Why the heck onScanFailed() isn't called from android system... but that's another story.

@DmytroBatyuk
Copy link

@philips77 I have checked the readme file and seems you have forgotten to add information been discussed here

@DmytroBatyuk
Copy link

My implementation to handle this is below

`

private const val NUM_SCAN_DURATIONS_KEPT = 5
private const val EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000L
private const val SCHEDULE_START_SCAN_WHEN_STARTED_TOO_FREQUENTLY = true

private class SafeScanner {
    private val scanner = BluetoothLeScannerCompat.getScanner()
    private val handler = Handler(Looper.myLooper()!!)

    private val startTimes = LinkedList<Long>()

    fun start(filters: List<ScanFilter>, settings: ScanSettings, callback: ScanCallback) {
        val now = System.currentTimeMillis()
        startTimes.removeIf { now - it > EXCESSIVE_SCANNING_PERIOD_MS }
        if (startTimes.size >= NUM_SCAN_DURATIONS_KEPT) {
            Log.e("BLE", "startScan: too frequent, $startTimes")
            callback.onScanFailed(ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY)

            if (SCHEDULE_START_SCAN_WHEN_STARTED_TOO_FREQUENTLY) {
                val delay = startTimes.first + EXCESSIVE_SCANNING_PERIOD_MS - now + 2_000
                Log.e("BLE", "startScan: schedule start after $delay ms")
                handler.postDelayed({
                    start(filters, settings, callback)
                }, delay)
            }
        } else {
            startTimes.addLast(now)
            scanner.startScan(filters, settings, callback)
            Log.e("BLE", "startScan: $startTimes")
        }
    }

    fun stop(callback: ScanCallback) {
        handler.removeCallbacksAndMessages(null)
        scanner.stopScan(callback)
    }
}

`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants