2

I am using RxAndroidBle library:

This code is working fine as expected, on click of Connect button on UI, it establishes connection.

Issue coming up when I wanted the auto-connect to the device when the device comes back to range. I don’t want to click on Connect button again.

is there any functionality exists like that ? does ’true’ flag helps me here, if yes, how to implement it ? Suggestion please. rxBleDevice.establishConnection(true);

If I use rxBleDevice.establishConnection(true), instead of ‘false’, I am getting below error, please help:

RxBleLog.setLogLevel(RxBleLog.VERBOSE);
scanSubscription = rxBleClient.scanBleDevices(
        new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                .build(),
        new ScanFilter.Builder().setDeviceName("mydevice").build()
).take(1).subscribe(
        scanResult -> {
                final RxBleDevice rxBleDevice = scanResult.getBleDevice();
                // connect to device
                final Observable<RxBleConnection> connection = rxBleDevice.establishConnection(false);
                connection
                        .subscribe(rxBleConnection -> {
                                    DeviceInformation deviceInformation = new DeviceInformation();
                                    deviceInformation.setName(rxBleDevice.getName());
                                    scanSubscription.unsubscribe();
                                },
                                throwable -> {
                                    throwable.printStackTrace();
                                    System.out.println(throwable);
                                }

                        );
        }
);


false
------

D/RxBle#ClientOperationQueue:   QUEUED ScanOperationApi21(226148850)
D/RxBle#ClientOperationQueue:  STARTED ScanOperationApi21(226148850)
I/Choreographer: Skipped 240 frames!  The application may be doing too much work on its main thread.
I/RxBle#QueueOperation: Scan operation is requested to start.
D/BluetoothLeScanner: onClientRegistered() - status=0 clientIf=6
D/RxBle#ClientOperationQueue: FINISHED ScanOperationApi21(226148850)
D/BluetoothLeScanner: onScanResult() - ScanResult{mDevice=6F:AE:DE:3E:2E:C3, mScanRecord=ScanRecord [mAdvertiseFlags=26, mServiceUuids=[00001805], mManufacturerSpecificData={}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=101-1], mRssi=-92, mTimestampNanos=1043849718860030}
D/RxBle#ClientOperationQueue:   QUEUED ConnectOperation(890174706)
D/RxBle#ClientOperationQueue:  STARTED ConnectOperation(890174706)
V/RxBle#BleConnectionCompat: Connecting without reflection
D/BluetoothGatt: connect() - device: 6F:AE:DE:3E:2E:C3, auto: false
D/BluetoothGatt: registerApp()
D/BluetoothGatt: registerApp() - UUID=542bd417
D/BluetoothGatt: onClientRegistered() - status=0 clientIf=7
D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=7 device=6F:AE:DE:3E:2E:C3
D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
D/RxBle#ClientOperationQueue: FINISHED ConnectOperation(890174706)
I/RxBle#CancellableSubscription: Scan operation is requested to stop.
D/RxBle#ConnectionOperationQueue:   QUEUED ServiceDiscoveryOperation(888759094)
D/RxBle#ConnectionOperationQueue:  STARTED ServiceDiscoveryOperation(888759094)
D/BluetoothGatt: discoverServices() - device: 6F:AE:DE:3E:2E:C3
D/BluetoothGatt: onSearchComplete() = Device=6F:AE:DE:3E:2E:C3 Status=0
D/RxBle#BluetoothGatt: onServicesDiscovered status=0
D/BluetoothGatt: setCharacteristicNotification() - uuid: b31e89de enable: true
D/RxBle#ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(888759094)


true
------
D/RxBle#ClientOperationQueue:   QUEUED ScanOperationApi21(226148850)
D/RxBle#ClientOperationQueue:  STARTED ScanOperationApi21(226148850)
I/Choreographer: Skipped 207 frames!  The application may be doing too much work on its main thread.
I/RxBle#QueueOperation: Scan operation is requested to start.
D/BluetoothLeScanner: onClientRegistered() - status=0 clientIf=6
D/RxBle#ClientOperationQueue: FINISHED ScanOperationApi21(226148850)
D/BluetoothLeScanner: onScanResult() - ScanResult{mDevice=6F:AE:DE:3E:2E:C3, mScanRecord=ScanRecord [mAdvertiseFlags=26, mServiceUuids=[00001805], mManufacturerSpecificData={}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=101-1], mRssi=-81, mTimestampNanos=1043935540628438}
D/RxBle#ClientOperationQueue:   QUEUED ConnectOperation(890174706)
D/RxBle#ClientOperationQueue:  STARTED ConnectOperation(890174706)
V/RxBle#ConnectOperation: Trying to connectGatt using reflection.
V/RxBle#BleConnectionCompat: Found constructor with args count = 4
V/RxBle#BleConnectionCompat: Connecting using reflection
D/BluetoothGatt: connect() - device: 6F:AE:DE:3E:2E:C3, auto: true
D/BluetoothGatt: registerApp()
D/BluetoothGatt: registerApp() - UUID=125a849aa38e
D/BluetoothGatt: onClientRegistered() - status=0 clientIf=7
D/BluetoothGatt: onClientConnectionState() - status=6 clientIf=7 device=6F:AE:DE:3E:2E:C3
D/RxBle#BluetoothGatt: onConnectionStateChange newState=0 status=6
W/System.err: BleDisconnectedException{bluetoothDeviceAddress='6F:AE:DE:3E:2E:C3'}
W/System.err:     at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$2.onConnectionStateChange(RxBleGattCallback.java:76)
W/System.err:     at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:181)
W/System.err:     at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
W/System.err:     at android.os.Binder.execTransact(Binder.java:446)
I/System.out: BleDisconnectedException{bluetoothDeviceAddress='6F:AE:DE:3E:2E:C3'}
I/RxBle#ConnectionOperationQueue: Connection operations queue to be terminated (6F:AE:DE:3E:2E:C3)
D/RxBle#Executors$RunnableAdapter: Terminated.
D/RxBle#ClientOperationQueue:   QUEUED DisconnectOperation(550952755)
D/RxBle#ClientOperationQueue: FINISHED ConnectOperation(890174706)
D/RxBle#ClientOperationQueue:  STARTED DisconnectOperation(550952755)
D/BluetoothManager: getConnectionState()
D/BluetoothManager: getConnectedDevices
D/BluetoothGatt: close()
D/BluetoothGatt: unregisterApp() - mClientIf=7
D/RxBle#ClientOperationQueue: FINISHED DisconnectOperation(550952755)-
Molay
  • 1,154
  • 2
  • 19
  • 42
  • There is no information about what caused the disconnection. Could you add logs when `RxBleLog.setLogLevel(RxBleLog.VERBOSE)` is set? `autoConnect=true` should work just as fine as on the vanilla API. Unfortunately it is often bugged when `autoConnect=true`. Also stop the `scanSubscription` before trying to connect—Android sometimes do not handle well scan and connection at the same time. – Dariusz Seweryn Dec 06 '17 at 12:18
  • updated the original post with logs for false and true, please check. – Molay Dec 06 '17 at 15:10
  • In the logs there is nothing that would point to the cause. Bummer that they do not have information about time. Maybe HCI log would contain some more info. Have you tried adding `.take(1)` just after `.scanBleDevices()`? – Dariusz Seweryn Dec 07 '17 at 11:09
  • yes, have tried that too, updated original post, no luck. – Molay Dec 07 '17 at 12:11
  • What is OS version and the handset model? – Dariusz Seweryn Dec 08 '17 at 21:38
  • macOS Sierra Version 10.12.6, Android studio 3.0.1, hand set model :Moto E, Android version 5.0.2 – Molay Dec 15 '17 at 04:14

1 Answers1

3

There are multiple possible solutions to achieve the expected behaviour so the action will start on the first click on Connect button and if the connection will be lost to reconnect without user interaction.

What you basically want is to:

  1. Scan a single device
  2. When device is scanned—connect to it
  3. When connected—do your stuff
  4. If an error will happen—retry connection

There is a bug in Android regarding connecting to a device after BluetoothAdapter being switched on that needs to be addressed by scanning the device first if the disconnection happened because adapter was switched off. We will just complete a part of the flow in this situation and repeat from the beginning.

Observable<Boolean> canUseBleObservable = canUseBle();
subscription = scanSingleDevice() // first scan the device
        .flatMapObservable(this::connectAndDoStuff) // when scanned connect and do your stuff
        .takeUntil(canUseBleObservable.takeFirst(isReady -> !isReady)) // if the BLE will be not ready (off) then unsubscribe from scanning and connecting
        .delaySubscription(canUseBleObservable.takeFirst(isReady -> isReady)) // delay subscription to scanning and connecting till BLE is ready (subscribing goes from bottom to top)
        .retry() // if scan will emit an error (connection should not as it has `.retry()`) just resubscribe to the upstream
        .repeatWhen(observable -> observable) // if the upstream will complete (due to `.takeUntil()` as `connectAndDoStuff` does not complete on it's own)—resubscribe to the upstream
        .subscribe(
                aVoid -> { /* consume */ } // `aVoid` should be changed to your model/events emitted by `.connectAndDoStuff`
                // throwable -> {  } => should not happen since there is `retry()` in the upstream
        );

Other building blocks could look like this:

private Observable<Boolean> canUseBle() {
    return rxBleClient.observeStateChanges()
            .share() // share observing state changes
            .startWith(Observable.fromCallable(() -> rxBleClient.getState())) // on each subscription emit the current state
            .map(state -> state == RxBleClient.State.READY); // map to `true` when ready
}

private Single<RxBleDevice> scanSingleDevice() {
    return rxBleClient.scanBleDevices( // scan the device
            new ScanSettings.Builder().build(),
            new ScanFilter.Builder().setDeviceName("mydevice").build()
    )
            .map(ScanResult::getBleDevice)
            .take(1) // after the first device being scanned stop the scan
            .toSingle();
}

private Observable<Void> connectAndDoStuff(RxBleDevice rxBleDevice) {
    return rxBleDevice.establishConnection(false)
            .flatMap(rxBleConnection -> {
                // do your stuff
                return Observable.<Void>empty();
            })
            .repeat(); // if any error (going out of range) will happen then resubscribe from `.establishConnection()`
}

This is a simple decomposition of your use case. One could further complicate the flow if needed.

Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21