Add workaround: Android API returns empty services list
Summary
I use this library, and in my project I need to disconnect and reconnect to the device really fast. But in rarely case I encounter a library bug. When I disconnect and reconnect lot of time on the same device, the library return an empty services list.
Library version
implementation 'com.polidea.rxandroidble2:rxandroidble:1.6.0'
and
implementation 'com.polidea.rxandroidble2:rxandroidble:1.7.0'
Steps to reproduce actual result
Connect and Disconnect until you reproduce this issue.
Minimum code snippet reproducing the issue
val obs = rxBleDevice.establishConnection(false).retryWhen {
it.flatMap {
Timber.e(it)
Observable.timer(200, TimeUnit.MILLISECONDS)
}
}.compose(ReplayingShare.instance()).doOnNext {
Timber.d("connection emitted ${++counter}")
}.doFinally {
Timber.d("connection closed ${--counter}")
}
val disposable = obs.flatMapSingle {
it.discoverServices()
}.doOnDispose {
connections.remove(mac)
}.subscribe({
Timber.d("first Connection was created")
}, { Timber.d(it) }).addTo(oneTimeDisposable)
Logs from the application running with setting RxBleLog.setLogLevel(RxBleLog.VERBOSE)
Actual result
Library return empty services list.
Expected result
Library return always good services list.
Hello,
This looks like an Android API bug. While it is possible for the library to introduce some mitigations (i.e. retrying the discovery process in this situation) they are likely to fail and not desirable by every user.
Could you add information about what device / OS you have encountered this bug?
Unfortunately for now you have to embrace this Android flaw and create a workaround...
I will leave this as a feature request and hopefully will have time for it once the new API will be available (as it will require more knobs in the API)
Best Regards
Hello,
Thanks for you answer.
I have encountered this bug with samsung galaxy S8 with android 8.0 and Huawei P9 lite with android 7.0.
Until the new version is released, do you have some advices how to retry the discovering of services only when list is empty in my code ?
Best Regards.
Currently retry would need to be implemented in a RxBleCustomOperation on the RxBleConnection.queue() function as the internal logic treats an empty list of services as a valid result and caches it.
I have update my code to around this problem with actual version.
fun getConnectionOrConnect(mac: MacAdress): Observable<RxBleConnection> {
synchronized(this) {
RxBleClient.setLogLevel(RxBleLog.VERBOSE)
Timber.d("$this $mac getconnectionOrConnect")
connections[mac]?.disposableDisconnect?.dispose()
return connections.getOrPut(mac) {
val nbTryDiscoverServices = AtomicInteger(10)
val rxBleDevice = bleClient.getBleDevice(mac)
Timber.d("$this $mac creating connection")
val obs = rxBleDevice.establishConnection(false).flatMap {
Observable.just(it).zipWith(it.discoverServices().toObservable())
}.map { (connection, rxbleServices) ->
if (rxbleServices.bluetoothGattServices.isEmpty()) {
throw EmptyBleServices(rxbleServices)
}
connection
}.retryWhen {
it.flatMap {
Timber.d(it)
if (it is EmptyBleServices) {
if (nbTryDiscoverServices.getAndDecrement() < 0) {
Events.eventNbTryForDiscoverServiceIsOut.set(true)
Observable.error<Throwable>(it)
} else {
Observable.timer(500, TimeUnit.MILLISECONDS)
}
} else {
Observable.timer(500, TimeUnit.MILLISECONDS)
}
}
}.compose(ReplayingShare.instance())
val disposable = obs.doOnDispose {
connections.remove(mac)
}.subscribe({
Timber.d("first Connection was created")
}, { Timber.d(it) }).addTo(oneTimeDisposable)
RefConnect(obs, disposable)
}.obs
}
}