0

I am working on the distance estimation of BLE Beacons from the Android phone. I have developed my own algorithms for distance estimation based on RSSI. (I shall roll out the distance calculation algorithms in the form of a library soon). For calculations, phone needs a huge number of advertisement packets from the beacons around.

So far, I have tested the code with normal practices for BLE scanning. As of now I have written the code for target API Level 19. Following is a part of the code I am working on, where I start the scan for beacons and stop it after 10s.

private void scanLeDevice(final boolean enable) {
    if (enable) {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                bluetoothAdapter.stopLeScan(LEScanCallback);
            }
        }, 10000L);

        bluetoothAdapter.startLeScan(LEScanCallback);
    }
}
private BluetoothAdapter.LeScanCallback LEScanCallback =
        new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord){   
                display(rssi);
                //append to an arrayList for further processing
            }
        };

Currently I am doing most of the things on UI thread.I have to achieve the functionality in the following way by creating more threads.

  1. UI Thread should be kept free of all the calculations or LeScans.
  2. PacketReaderThread - A separate thread for scanning packets and keep appending them to an arrayList (I will wipeout unnecessary data from arrayList upon its consumption in algorithms). Rather than stopping the scan after certain time, I want to keep the scan ON for infinite time to keep working in real-time, as far as app is running. So I might use IntentService for this.
  3. DistanceCalculatorThread (or AsyncTask)- ArrayList object(containing scanned packets) will be synchronized between this and the PacketReaderThread for taking/wiping-out data packets and inform about distance calculation to UI.

I saw the implementation of BluetoothAdapter.LeScanCallback on Grepcode I have following questions.

  1. Is BluetoothAdapter.LeScanCallback implicitly bound/tied to main/UI thread or is it portable to any other thread ?
  2. If I move both of the implementations of the code above to some other thread, will the callback work on that thread or is it going to be bound to main/ui thread ? (Since I need packet scanning on separate thread, I need to know about bluetoothAdapter.startLeScan(LEScanCallback) and BluetoothAdapter.LeScanCallback)
    (Note - I have already followed this question, in that question, the answer-seeker himself states that callback is on main thread, so I didn't get the actual answer. Also I saw altBeacon specs and studied their reference app, it also uses CycledScan mechanism with scan-stop-scanAgain way)
Community
  • 1
  • 1
Tejas
  • 21
  • 5

1 Answers1

1

Yes, the Android bluetooth scan callbacks are always made on the main thread. This is true with both the 4.x APIs and the 5+ APIs. If you are doing significant processing with information in the callback, it is wise to pass that to a different thread as you describe. If you do not the app UI will become laggy and the bluetooth processing may even back up and you will see errors in the logs.

I don't believe this is officially documented anywhere, but years of experience working with these callbacks has always shown that to be true. There is no way to configure the scanning so the callbacks happen on a different thread. The best practice is to simply make a single call to a different thread to do the processing and quickly exit the callback.

This is what is done in the Android Beacon Library as shown here. Note that the body of the callback simply uses an AsyncTask an executes it on a background thread with this line:

new ScanProcessor(...).executeOnExecutor(mExecutor,
                    new ScanData(device, rssi, scanRecord));

Also, it is important to note that while there is no reason to stop the scan on newer Android devices, on some older devices you will only get one callback per unique beacon mac address if the beacon advertisement is connectable. The only way around this on those devices is to stop and restart the scan to get an additional callback.

Good luck with your distance algorithm -- if you have good results and would be interested in sharing for the Android Beacon Library, I'd love to discuss.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Yes, I do have good results. RSSI to distance mapping converges faster than Locate ;) You have written that library so well, I have studied it. Thanks for the answer David, so I shall use LooperThread. If you find any official documentation on the callbacks, please let know. And we will definitely discuss once I complete the demo in a few weeks. – Tejas Jul 08 '16 at 14:38