0

I am working on Android BLE using RxAndroidBle library. The BLE device has a timeout of 5 min if there is no interaction. i.e. The device will disconnect automatically if there is no read/write operations for 5 minutes. I am getting BleGattException when timeout disconnection happens.

     W/System.err: BleGattException{macAddress=24:71:89:FE:A0:88, status=19 (0x13 -> https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h), bleGattOperationType=BleGattOperation{description='CONNECTION_STATE'}}
 W/System.err:     at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:233)
 W/System.err:     at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$200(RxBleGattCallback.java:35)
 W/System.err:     at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$3.onConnectionStateChange(RxBleGattCallback.java:86)
 W/System.err:     at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:228)
 W/System.err:     at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
 W/System.err:     at android.os.Binder.execTransact(Binder.java:453)

 W/BluetoothGatt: Unhandled exception in callback
                                                                  rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
                                                                      at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:194)
                                                                      at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:115)
                                                                      at rx.observers.Subscribers$5.onError(Subscribers.java:230)
                                                                      at rx.observers.Subscribers$5.onError(Subscribers.java:230)
                                                                      at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
                                                                      at rx.observers.Subscribers$5.onError(Subscribers.java:230)
                                                                      at rx.observers.Subscribers$5.onError(Subscribers.java:230)
                                                                      at rx.observers.Subscribers$5.onError(Subscribers.java:230)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:266)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:818)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:579)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:568)
                                                                      at rx.internal.operators.OperatorMerge$InnerSubscriber.onError(OperatorMerge.java:852)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:266)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:818)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:579)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:568)
                                                                      at rx.internal.operators.OperatorMerge$InnerSubscriber.onError(OperatorMerge.java:852)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:266)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:818)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:579)
                                                                      at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:568)
                                                                      at rx.internal.operators.OperatorMerge$InnerSubscriber.onError(OperatorMerge.java:852)
                                                                      at rx.internal.operators.NotificationLite.accept(NotificationLite.java:132)
                                                                      at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitNext(SubjectSubscriptionManager.java:253)
                                                                      at rx.subjects.BehaviorSubject.onError(BehaviorSubject.java:141)
                                                                      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusError(RxBleGattCallback.java:241)
                                                                      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:233)
                                                                      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$200(RxBleGattCallback.java:35)
                                                                      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$3.onConnectionStateChange(RxBleGattCallback.java:86)
                                                                      at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:228)
                                                                      at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
                                                                      at android.os.Binder.execTransact(Binder.java:453)
                                                                   Caused by: rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
                                                                      at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:187)
                                                                      at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:115)
                                                                      at rx.exceptions.Exceptions.throwOrReport(Exceptions.java:216)
                                                                      at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139)
                                                                      at rx.internal.operators.OperatorDistinctUntilChanged$1.onNext(OperatorDistinctUntilChanged.java:96)
                                                                      at rx.internal.operators.NotificationLite.accept(NotificationLite.java:135)
                                                                      at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitNext(SubjectSubscriptionManager.java:253)
                                                                      at rx.subjects.BehaviorSubject.onNext(BehaviorSubject.java:160)
                                                                      at com.polidea.rxandroidble.internal.RxBleDeviceImpl$1$1.call(RxBleDeviceImpl.java:77)
                                                                      at rx.subscriptions.BooleanSubscription.unsubscribe(BooleanSubscription.java:71)
                                                                      at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:136)
                                                                      at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:125)

Once the device disconnects due to timeout, When I scan again I could find the device in the near by available devices as expected. But when I try to connect to the same device, it throws an exception telling device is already connected

 W/System.err: BleAlreadyConnectedException{macAddress=24:71:89:FE:99:E3}
 W/System.err:     at com.polidea.rxandroidble.internal.RxBleDeviceImpl.lambda$establishConnection$3(RxBleDeviceImpl.java:54)
 W/System.err:     at com.polidea.rxandroidble.internal.RxBleDeviceImpl$$Lambda$1.call(Unknown Source)
 W/System.err:     at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
 W/System.err:     at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
 W/System.err:     at rx.Observable.subscribe(Observable.java:10236)
 W/System.err:     at rx.Observable.subscribe(Observable.java:10203)
 W/System.err:     at rx.Observable.subscribe(Observable.java:10043)

But I am able to connect to the device if I turn off and On bluetooth in my mobile or if I kill the app and restart.

And re-connecting is working when I manually disconnect the device and connect again. The issue is only when time-out disconnection happens.

Can any one tell me why is this happening?

VishnuSP
  • 599
  • 5
  • 16

1 Answers1

1

Judging from the error and the implementation of the .establishConnection() function you do not unsubscribe from the previous Observable that was returned.

@Override
public Observable<RxBleConnection> establishConnection(final boolean autoConnect) {
    return Observable.defer(new Func0<Observable<RxBleConnection>>() {
        @Override
        public Observable<RxBleConnection> call() {

            if (isConnected.compareAndSet(false, true)) {
                return connector.prepareConnection(autoConnect)
                        .doOnSubscribe(new Action0() {
                            @Override
                            public void call() {
                                connectionStateSubject.onNext(CONNECTING);
                            }
                        })
                        .doOnNext(new Action1<RxBleConnection>() {
                            @Override
                            public void call(RxBleConnection rxBleConnection) {
                                connectionStateSubject.onNext(CONNECTED);
                            }
                        })
                        .doOnUnsubscribe(new Action0() {
                            @Override
                            public void call() {
                                connectionStateSubject.onNext(DISCONNECTED);
                                isConnected.set(false);
                            }
                        });
            } else {
                return Observable.error(new BleAlreadyConnectedException(bluetoothDevice.getAddress()));
            }
        }
    });
}

Once you unsubscribe from the previous one you should be able to establish a new connection.

Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21
  • Thanks for the information @s_noopy. But I am already unsubscibing before next connection. And re-connecting is working when I manually disconnect the device and connect again. The issue is only when time-out disconnection happens. – VishnuSP Apr 21 '17 at 12:05
  • Once the peripheral-side disconnection happens – the `Observable` from `.establishConnection()` should emit a `BleDisconnectedException` - does it happen? – Dariusz Seweryn Apr 21 '17 at 12:15
  • No. I'm not getting BleDisconnectedException, but BleGattException 'W/System.err: BleGattException{status=19, bleGattOperation=BleGattOperation{description='CONNECTION_STATE'}} W/System.err: at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:248)' – VishnuSP Apr 24 '17 at 04:51
  • This is a different exception than the one from the original post. Update the original one with new informations. The `BleGattException` object - when printed - sends you to a site `https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h` where you can find the answer about a specific error `#define GATT_CONN_TERMINATE_PEER_USER HCI_ERR_PEER_USER /* 0x13 connection terminate by peer user */` – Dariusz Seweryn Apr 24 '17 at 06:56
  • Yes. I have checked that and it indicates that the connection is terminated. So if I try to establish the connection again, it should happen. But it's not happening. That is my issue. Any idea on why is it so? – VishnuSP Apr 24 '17 at 08:41
  • The error is indicating that the peripheral you are connecting to is dropping the connection. Connecting again does not necessarily says it will be successful. It may be dropped again by the peer. – Dariusz Seweryn Apr 24 '17 at 09:41
  • But I am able to connect to the peer if I turn off and On bluetooth in my mobile or if I kill the app and restart. So I don't think it is dropped again by peer. – VishnuSP Apr 24 '17 at 09:46
  • This may be a bug in the Android BLE stack: https://issuetracker.google.com/issues/37029019 Unfotunately I cannot tell more without more verbose logs from your app. – Dariusz Seweryn Apr 24 '17 at 11:56