Search code examples
androidbluetooth-lowenergyandroid-bluetoothrxandroidble

RxAndroidBle : Periodic advertising scan


I need to scan ble advertising permanently while i am not connected to my device to find it and know when connect to it (specific product). This scan is realized in foreground service to match 8.0 prerequisite.

To preserve a few battery, i want scan periodically (while respect the Android 7 limit of start amount/time). I see 2 differents implementations :

  • Start the scan each X seconds and be able to stop it after Y seconds, the wait time will be X-Y.

    ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ Scan‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ Wait‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌Scan‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ Wait
    |--------------|------|--------------|------|---
    |---------X-------->|---------X-------->|---
    |------Y----->‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ |-----Y----->‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ |---


  • Start the scan for X seconds, wait for Y seconds, start the scan for X seconds etc..

    ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ Scan‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ Wait‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌Scan‌‌ ‌‌ ‌‌ ‌‌ ‌‌ ‌‌ Wait
    |--------------|------|--------------|------|---
    |------X---->|--Y->|------X---->|--Y->|---

I want to do it with Rx style and not with the simple timer (i am not an RX expert). I don't know what solution is better/easier to be developed. This must be transparent to the client and must keep the original RxAndroidBle working, subscribe = start the process and unsubscribe = stop the process.

I suppose I have to use Obersable.interval but i don't see how to manage this Any snippet/ example?


Solution

  • The original Android BLE API has BluetoothLeScanner class which allows to start a scan using one of four modes:

    • SCAN_MODE_LOW_LATENCY—the scan is continuous
    • SCAN_MODE_BALANCED—the scan is working for 2 seconds every 5 seconds
    • SCAN_MODE_LOW_POWER—the scan is working for 0.5 second every 5 seconds
    • SCAN_MODE_OPPORTUNISTIC—the scan is working only when a different application starts it

    The time measurements were performed by Nordic Semiconductors employee and released in a form of a comprehensive guide for Android BLE developers.

    The RxAndroidBle library also has a function RxBleClient.scanBleDevices(ScanSettings, ScanFilter...) which accepts the same modes inside a ScanSettings object.

    If one would need to achieve different parameters of scanning it is possible to simply use a combintaion of Observable.takeUntil() and Observable.repeatWhen():

    Observable<ScanResult> lowLatencyScanObservable = rxBleClient.scanBleDevices(
      new ScanSettings.Builder()
        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // make sure that the scanning will be always on by default
        .build()
    );
    
    Observable<ScanResult> periodicalScan = lowLatencyScanObservable
      .takeUntil(Observable.timer(SCAN_TIME, SCAN_TIMEUNIT)) // scan for a specific amount of time and then unsubscribe to the upstream
      .repeatWhen(completionObservable -> completionObservable.delay(PAUSE_TIME, PAUSE_TIMEUNIT)); // when the upstream will complete because of `takeUntil()` wait for the pause time and resubscribe to the upstream
    

    One would still need to remember about the undocumented "feature" of API 24+ which prevents starting more than 5 scans during a 30 seconds period.