1

I am trying to scan for BLE devices once I find my device or after 10 sec, I am trying to stop the scan. But for some reason the onBatchScanResults is getting called indefinitely.

I found that even after stopping the scan the onBatchScanResults will be called until the queue of scanned results are drained. But in my case it is never getting stopped. Below is the code how I am trying to achieve this.

public void scan() {
    scanner = BluetoothLeScannerCompat.getScanner();
    final ScanSettings settings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).setReportDelay(1000).setUseHardwareBatchingIfSupported(false).build();
    final List<ScanFilter> filters = new ArrayList<>();
    filters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid(getFilterUUID()))
            .build());
    Log.e(TAG, "Scanning.....");
    scanner.startScan(filters, settings, scanCallback);

    mIsScanning = true;
    mHandler.postDelayed(() -> {
        if (mIsScanning) {
            showToast("Not able to find any new device.");
            stopScan();
        }
    }, SCAN_DURATION);
}

private void stopScan() {
    if (mIsScanning) {
        final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
        scanner.stopScan(scanCallback);
        mIsScanning = false;
        closeWaitDialog();
}

private ScanCallback scanCallback = new ScanCallback() {
    @Override
    public void onScanResult(final int callbackType, final ScanResult result) {
        // do nothing
        stopScan();
    }

    @Override
    public void onBatchScanResults(final List<ScanResult> results) {
        Log.e(TAG, results.toString() + " mIsScanning " + mIsScanning);
        if (results.size() == 1) {
            stopScan();
            ScanResult scanResult = results.get(0);
            launchSomeActivity();
        } else if (results.size() > 1) {
            stopScan();
            showToast("Too many new devices. Please scan one device at a time.");
        } else {
            // Do nothing. As we will stop anyway stop scanning after 5 sec.
        }
    }

    @Override
    public void onScanFailed(final int errorCode) {
        // should never be called
    }
};

Any help is greatly appreciated.

tharun
  • 348
  • 6
  • 15
  • Hello, I'm the author of Android Scanner Compat Library. Sorry for late response, but I've just seen your question. Did you manage to solve it? There is a new version of Scanner Compat Library 1.2.0 available since few days with improved synchronization. Could you check if it works for you? – philips77 Jan 17 '19 at 10:39
  • 1
    The issue should be fixed with https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library/pull/40 – philips77 Jan 18 '19 at 16:25
  • I haven't tried it. I will let you know once I have tried it. – tharun Jan 19 '19 at 17:45
  • @philips77 Apologies for the late reply. I tried checking with the latest version and the bug is fixed. – tharun Apr 29 '19 at 14:14
  • How do you use setUseHardwareBatchingIfSupported? It's not offered on the IDE, and there isn't documentation about it either: https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder – android developer Jan 26 '23 at 23:10

4 Answers4

1

First you are calling stopScan() way too many times. And in stopScan() you're creating new instance of BluetoothLeScannerCompat with the same name scanner. Plus, calling stopScan() from ScanCallback isn't a good practice. Here's the working example:

BluetoothLeScannerCompat scanner;
public void scan() {
    scanner = BluetoothLeScannerCompat.getScanner();
    ScanSettings settings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).setReportDelay(10)
            .build();
    List<ScanFilter> filters = new ArrayList<>();
    filters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid(mUUid)).build());
    scanner.startScan(filters,settings, scanCallback);


    final Handler handle = new Handler();
    handle.postDelayed(new Runnable() {
        @Override
        public void run() {
            stopScan();
        }
    },2000);
}

private void stopScan() {
    Log.i("Device Found: ", "Scan Stoped");
        scanner.stopScan(scanCallback);
}

private ScanCallback scanCallback = new ScanCallback() {
    @Override
    public void onScanResult(final int callbackType, final ScanResult result) {
    }

    @Override
    public void onBatchScanResults(final List<ScanResult> results) {
        Log.i("Device Found: ", results.size()+"");
    }

    @Override
    public void onScanFailed(final int errorCode) {
        // should never be called
    }
};

Logs

When you call StopScan() from scanCallback:

01-17 14:52:53.870 32434-32434/com.dleague.salman.example I/OnBatchScan:                     
Scanning...
01-17 14:52:53.870 32434-32434/com.dleague.salman.example I/StopScan:     
Scanning Stop...
01-17 14:52:53.881 32434-32434/com.dleague.salman.example I/OnBatchScan: 
Scanning...
01-17 14:52:53.881 32434-32434/com.dleague.salman.example I/StopScan: 
Scanning Stop...
01-17 14:52:53.892 32434-32434/com.dleague.salman.example I/OnBatchScan: 
Scanning...
01-17 14:52:53.892 32434-32434/com.dleague.salman.example I/StopScan: 
Scanning Stop...
01-17 14:52:53.902 32434-32434/com.dleague.salman.example I/OnBatchScan: 
Scanning...
01-17 14:52:53.902 32434-32434/com.dleague.salman.example I/StopScan: 
Scanning Stop...
01-17 14:52:53.912 32434-32434/com.dleague.salman.example I/OnBatchScan: 
Scanning...
01-17 14:52:53.912 32434-32434/com.dleague.salman.example I/StopScan: 
Scanning Stop...
01-17 14:52:53.923 32434-32434/com.dleague.salman.example I/OnBatchScan: 
Scanning...

And when you StopScan() from ScanPeriod Not within the scanCallback:

01-17 14:55:10.703 1673-1673/com.dleague.salman.example I/OnBatchScan: 
Scanning...
01-17 14:55:10.714 1673-1673/com.dleague.salman.example I/OnBatchScan:     
Scanning...
....
01-17 14:55:12.684 1673-1673/com.dleague.salman.example I/OnBatchScan: 
Scanning...
01-17 14:55:12.694 1673-1673/com.dleague.salman.example I/OnBatchScan: 
Scanning...
01-17 14:55:12.700 1673-1673/com.dleague.salman.example I/StopScan: Scanning 
Stop...
Salman Naseem
  • 462
  • 4
  • 17
  • 1
    Why wouldn't it be good practice to stop scan in scan callback? – Emil Jan 01 '18 at 12:32
  • Because in [Documentation](https://developer.android.com/guide/topics/connectivity/bluetooth-le.html) snippet shows how to start and stop a scan. And they said Scanning in Period is the good way. Plus check [this](https://devzone.nordicsemi.com/question/19156/start-stop-scanning-in-android-app/) answer. I can be wrong but that's my understanding and guide me if i'm wrong. – Salman Naseem Jan 01 '18 at 13:11
  • 2
    The documentation clearly says the scan should be stopped when the requested device has been found (there is obviously no reason to continue to scan then). This can of course be done from the callback. – Emil Jan 01 '18 at 15:27
  • What if requested device is not available... When scanning will stop? In a context of [Android BLE Scanner Compat library](https://github.com/NordicSemiconductor/Android-nRF-Toolbox/blob/master/app/src/main/java/no/nordicsemi/android/nrftoolbox/scanner/ScannerFragment.java#L207-L277), if you call `stopScan()` from `scanCallback` it wont work. Try to implement the example and stopScan with your best way. – Salman Naseem Jan 02 '18 at 07:18
  • I tried the above given solutions to no success, for some reason the scan never gets stopped. I used the same scanner, I even removed the stop scan call from onBatchScanResults() function But for some reason they don't seem to do the trick. – tharun Jan 17 '18 at 09:30
  • Well idk why its not working for you then, because i tested it before posting it. i will add some logs too. – Salman Naseem Jan 17 '18 at 09:46
  • Calling `stopScan()` from `scanCallback should work, there is no reason why wouldn't it. If it doesn't it's a bug in the library which I'll investigate. Version 1.2.0 was released couple of days ago which may have accidentally fixed the issue. – philips77 Jan 17 '19 at 10:42
  • @philips77 after investigation, let me know with your findings, regards. – Salman Naseem Jan 17 '19 at 14:07
  • The issue should be fixed with https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library/pull/40. Will be released in version 1.3.0. – philips77 Jan 18 '19 at 16:26
1

Just add .setUseHardwareBatchingIfSupported(true) to your ScanSettings if you want to stopScan when you are using setReportDelay > 0

ScanSettings settings = new ScanSettings.Builder()
            .setUseHardwareBatchingIfSupported(true)
            .setReportDelay(1000)
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .build();
MarsPeople
  • 1,772
  • 18
  • 30
  • This will just use native batching instead, which may solve the issue on Lollipop+, but not on previous versions. It's more a workaround. – philips77 Jan 17 '19 at 10:40
  • How do you use setUseHardwareBatchingIfSupported? It's not offered on the IDE, and there isn't documentation about it either: https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder – android developer Jan 26 '23 at 23:11
  • @androiddeveloper it was in 'no.nordicsemi.android.support.v18.scanner' package. you can find it here: https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library – MarsPeople Jan 28 '23 at 15:13
  • @MarsPeople So it's no more, right? – android developer Jan 29 '23 at 16:20
  • @androiddeveloper you can still use it, add implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0' to your build.gradle then use this code sample (don't forget the 'import no.nordicsemi.android.support.v18.scanner.ScanSettings'): https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library#sample – MarsPeople Jan 30 '23 at 18:41
  • @MarsPeople Oh that's not part of Google's SDKs... – android developer Jan 30 '23 at 18:47
  • @androiddeveloper yes i said that before above, 'ScanSettings', 'ScanFilter' is not native adnroid classes. They should be no.nordicsemi.android.support.v18.scanner.ScanSettings and no.nordicsemi.android.support.v18.scanner.ScanFilter – MarsPeople Jan 30 '23 at 19:12
  • @MarsPeople OK sorry – android developer Jan 31 '23 at 20:26
0

You are creating new instances of ScanCallback and losing reference to previous instances. That is why you are not able to stop ScanCallback for older instances. Just create one instance of ScanCallBack at the start of the class and use it across your scan logic.

lakshman.pasala
  • 565
  • 6
  • 16
  • That was my first thought too, but I am using the same instance of 'ScanCallback' all the time. – tharun Dec 27 '17 at 04:07
  • This piece `private ScanCallback scanCallback = new ScanCallback()` says otherwise. You are creating new instances of ScanCallback when you call scanCallback – lakshman.pasala Dec 27 '17 at 07:11
  • I am declaring the variable only once and using the same instance all the time. May be the code is little confusing, scanCallback variable is a global variable or am I missing something. – tharun Dec 27 '17 at 10:28
  • Where are you calling `scan( )` method? And how many times are you calling it? – lakshman.pasala Dec 27 '17 at 11:04
  • scan() method will be called from onClick method when user clicks to start the scan. – tharun Dec 28 '17 at 05:18
  • Basically I am following the example given here: https://github.com/NordicSemiconductor/Android-nRF-Toolbox/blob/master/app/src/main/java/no/nordicsemi/android/nrftoolbox/scanner/ScannerFragment.java#L207-L277 – tharun Dec 28 '17 at 05:24
0

For the sake of having an answer.

This bug is fixed in the latest version of the library(Scanner Compact Library 1.2.0).

tharun
  • 348
  • 6
  • 15