Giter Club home page Giter Club logo

rxandroidble's Introduction

RxAndroidBle Build Status Maven Central

Tailored software services including concept, design, development and testing

Introduction

RxAndroidBle is a powerful painkiller for Android's Bluetooth Low Energy headaches. It is backed by RxJava, implementing complicated APIs as handy reactive observables. The library does for you:

  • Fancy asynchronous operations support (read, write, notifications)
  • Threading management in order to meet Android contracts
  • Connection and operation error handling

For support head to StackOverflow #rxandroidble

RxAndroidBLE @ Mobile Central Europe 2016

RxAndroidBLE @ Mobile Central Europe 2016

Getting Started

The first step is to include RxAndroidBle into your project.

Gradle

If you use Gradle to build your project — as a Gradle project implementation dependency:

implementation "com.polidea.rxandroidble3:rxandroidble:1.18.1"

or for RxJava 2 based artifact

implementation "com.polidea.rxandroidble2:rxandroidble:1.18.1"

Maven

If you use Maven to build your project — as a Maven project dependency:

<dependency>
  <groupId>com.polidea.rxandroidble3</groupId>
  <artifactId>rxandroidble</artifactId>
  <version>1.18.1</version>
  <type>aar</type>
</dependency>

or for RxJava 2 based artifact

<dependency>
  <groupId>com.polidea.rxandroidble2</groupId>
  <artifactId>rxandroidble</artifactId>
  <version>1.18.1</version>
  <type>aar</type>
</dependency>

Snapshot

If your are interested in cutting-edge build you can get a x.y.z-SNAPSHOT version of the library. NOTE: Snapshots are built from the top of the master and develop branches and a subject to more frequent changes that may break the API and/or change behavior.

To be able to download it you need to add Sonatype Snapshot repository site to your build.gradle file:

maven { url "https://oss.sonatype.org/content/repositories/snapshots" }

Permissions

Android requires additional permissions declared in the manifest for an app to run a BLE scan since API 23 (6.0 / Marshmallow) and perform a BLE connection since API 31 (Android 12). RxAndroidBle provides a minimal set of commonly used bluetooth permissions for you in its AndroidManifest.xml. These permissions currently assume scanning is only used when the App is in the foreground, and that the App wants to derive the user's location from BLE signal (on API >= 23). Below are a number of additions you can make to your AndroidManifext.xml for your specific use case.

If you want to derive the user's location in your App

RxAndroidBle uses the uses-permission-sdk-23 tag to require location only on APIs >= 23, where it is required for BLE scanning. Additionally, in a future version of RxAndroidBle, these permissions will be restricted to only APIs 23-30. To ensure you can derive the user's location in your App with all API versions, and avoid any issues with merging of permissions when uploading to the Play Store, add the following to your AndroidManifest.xml:

<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="remove" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

If you do not want to derive user's location in your App

If you only want to scan for BLE peripherals and do not access location otherwise you can request only the required permissions by adding the following to your AndroidManifest.xml:

<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" tools:node="replace" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" tools:node="replace" />

If you want to scan in the background and support APIs 29 & 30

You should add the following to your AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="30" />

If you want to access the user's location in the background on APIs > 30, remove the android:maxSdkVersion attribute.

If you want to derive the user's location from BLE scanning in API >= 31

API 31 (Android 12) introduced new Bluetooth permissions. RxAndroidBle uses the android:usesPermissionFlags="neverForLocation" attribute on the BLUETOOTH_SCAN permission, which indicates scanning will not be used to derive the user's location, so location permissions are not required. If you need to locate the user with BLE scanning, use this instead, but keep in mind that you will still need ACCESS_FINE_LOCATION:

<uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:node="replace" />

If you do not need to connect to peripherals

You can remove the BLUETOOTH_CONNECT permission that RxAndroidBle requests in APIs >= 31:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove" />

Summary of available permissions

Scanning

A summary of available runtime permissions used for BLE scanning:

from API to API (inclusive) Acceptable runtime permissions
18 22 (No runtime permissions needed)
23 28 One of below:
- android.permission.ACCESS_COARSE_LOCATION
- android.permission.ACCESS_FINE_LOCATION
29 30 - android.permission.ACCESS_FINE_LOCATION
- android.permission.ACCESS_BACKGROUND_LOCATION*
31 current - android.permission.BLUETOOTH_SCAN
- android.permission.ACCESS_FINE_LOCATION**

* Needed if scan is performed in background

** Only needed if you want to obtain user's location with BLE scanning (BLUETOOTH_SCAN is not using neverForLocation attribute in your App)

Connecting

A summary of available runtime permissions used for BLE connections:

from API to API (inclusive) Acceptable runtime permissions
18 30 (No runtime permissions needed)
31 current - android.permission.BLUETOOTH_CONNECT

Usage

Obtaining the client

It's your job to maintain single instance of the client. You can use singleton, scoped Dagger component or whatever else you want.

RxBleClient rxBleClient = RxBleClient.create(context);

Turning the bluetooth on / off

The library does not handle managing the state of the BluetoothAdapter.
Direct managing of the state is not recommended as it violates the application user's right to manage the state of their phone. See Javadoc of BluetoothAdapter.enable() method.
It is the user's responsibility to inform why the application needs Bluetooth to be turned on and for ask the application's user consent.
It is possible to show a native activity for turning the Bluetooth on by calling:

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
int REQUEST_ENABLE_BT = 1;
context.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

Device discovery

Scanning devices in the area is simple as that:

Disposable scanSubscription = rxBleClient.scanBleDevices(
        new ScanSettings.Builder()
            // .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // change if needed
            // .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
            .build()
        // add filters if needed
)
    .subscribe(
        scanResult -> {
            // Process scan result here.
        },
        throwable -> {
            // Handle an error here.
        }
    );

// When done, just dispose.
scanSubscription.dispose();

For devices with API <21 (before Lollipop) the scan API is emulated to get the same behaviour.

Observing client state

On Android it is not always trivial to determine if a particular BLE operation has a potential to succeed. e.g. to scan on Android 6.0 the device needs to have a BluetoothAdapter, the application needs to have a granted runtime permission for either ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION, additionally Location Services need to be turned on. To be sure that the scan will work only when everything is ready you could use:

Disposable flowDisposable = rxBleClient.observeStateChanges()
    .switchMap(state -> { // switchMap makes sure that if the state will change the rxBleClient.scanBleDevices() will dispose and thus end the scan
        switch (state) {

            case READY:
                // everything should work
                return rxBleClient.scanBleDevices();
            case BLUETOOTH_NOT_AVAILABLE:
                // basically no functionality will work here
            case LOCATION_PERMISSION_NOT_GRANTED:
                // scanning and connecting will not work
            case BLUETOOTH_NOT_ENABLED:
                // scanning and connecting will not work
            case LOCATION_SERVICES_NOT_ENABLED:
                // scanning will not work
            default:
                return Observable.empty();
        }
    })
    .subscribe(
    	rxBleScanResult -> {
    	    // Process scan result here.
    	},
    	throwable -> {
    	    // Handle an error here.
    	}
    );

// When done, just dispose.
flowDisposable.dispose();

Connection

For further BLE interactions the connection is required.

String macAddress = "AA:BB:CC:DD:EE:FF";
RxBleDevice device = rxBleClient.getBleDevice(macAddress);

Disposable disposable = device.establishConnection(false) // <-- autoConnect flag
    .subscribe(
        rxBleConnection -> {
            // All GATT operations are done through the rxBleConnection.
        },
        throwable -> {
            // Handle an error here.
        }
    );

// When done... dispose and forget about connection teardown :)
disposable.dispose();

Auto connect

From BluetoothDevice.connectGatt() Javadoc:

autoConnect boolean: Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true).

Auto connect concept may be misleading at first glance. With the autoconnect flag set to false the connection will end up with an error if a BLE device is not advertising when the RxBleDevice#establishConnection method is called. From platform to platform timeout after which the error is emitted differs, but in general it is rather tens of seconds than single seconds (~30 s).

Setting the auto connect flag to true allows you to wait until the BLE device becomes discoverable. The RxBleConnection instance won't be emitted until the connection is fully set up. From experience it also handles acquiring wake locks, so it's safe to assume that your Android device will be woken up after the connection has been established - but it is not a documented feature and may change in the future system releases. Unlike the native Android API, if autoConnect=true while using this library there will be NO attempts to automatically reconnect if the original connection is lost.

Be careful not to overuse the autoConnect flag. On the other side it has negative impact on the connection initialization speed. Scanning window and interval is lowered as it is optimized for background use and depending on Bluetooth parameters it may (and usually do) take more time to establish the connection.

Read / write operations

Read

device.establishConnection(false)
    .flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
    .subscribe(
        characteristicValue -> {
            // Read characteristic value.
        },
        throwable -> {
            // Handle an error here.
        }
    );

Write

device.establishConnection(false)
    .flatMapSingle(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, bytesToWrite))
    .subscribe(
        characteristicValue -> {
            // Characteristic value confirmed.
        },
        throwable -> {
            // Handle an error here.
        }
    );

Multiple reads

device.establishConnection(false)
    .flatMap(rxBleConnection -> Single.zip(
        rxBleConnection.readCharacteristic(firstUUID),
        rxBleConnection.readCharacteristic(secondUUID),
        YourModelCombiningTwoValues::new
    ))
    .subscribe(
        model -> {
            // Process your model.
        },
        throwable -> {
            // Handle an error here.
        }
    );

Long write

device.establishConnection(false)
    .flatMap(rxBleConnection -> rxBleConnection.createNewLongWriteBuilder()
        .setCharacteristicUuid(uuid) // required or the .setCharacteristic()
        // .setCharacteristic() alternative if you have a specific BluetoothGattCharacteristic
        .setBytes(byteArray)
        // .setWriteOperationRetryStrategy(retryStrategy) // if you'd like to retry batch write operations on failure, provide your own retry strategy
        // .setMaxBatchSize(maxBatchSize) // optional -> default 20 or current MTU
        // .setWriteOperationAckStrategy(ackStrategy) // optional to postpone writing next batch
        .build()
    )
    .subscribe(
        byteArray -> {
            // Written data.
        },
        throwable -> {
            // Handle an error here.
        }
    );

Read and write combined

device.establishConnection(false)
    .flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid)
        .doOnSuccess(bytes -> {
            // Process read data.
        })
        .flatMap(bytes -> rxBleConnection.writeCharacteristic(characteristicUuid, bytesToWrite))
    )
    .subscribe(
        writeBytes -> {
            // Written data.
        },
        throwable -> {
            // Handle an error here.
        }
    );

Change notifications

device.establishConnection(false)
    .flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristicUuid))
    .doOnNext(notificationObservable -> {
        // Notification has been set up
    })
    .flatMap(notificationObservable -> notificationObservable) // <-- Notification has been set up, now observe value changes.
    .subscribe(
        bytes -> {
            // Given characteristic has been changes, here is the value.
        },
        throwable -> {
            // Handle an error here.
        }
    );

Observing connection state

If you want to observe changes in device connection state just subscribe like below. On subscription you will receive the most current state instantly.

device.observeConnectionStateChanges()
    .subscribe(
        connectionState -> {
            // Process your way.
        },
        throwable -> {
            // Handle an error here.
        }
    );

Logging

For connection debugging you can use extended logging

RxBleClient.setLogLevel(RxBleLog.DEBUG);

By default RxBleLog uses logcat to print the messages. You can provide your own logger implementation to forward it to other logging libraries such as Timber.

RxBleLog.setLogger((level, tag, msg) -> Timber.tag(tag).log(level, msg));

Error handling

Every error you may encounter is provided via onError callback. Each public method has JavaDoc explaining possible errors.

Observable behaviour

From different interfaces, you can obtain different Observables which exhibit different behaviours. There are two types of Observables that you may encounter.

  1. Multiple values - i.e. RxBleClient.scan(), RxBleDevice.observeConnectionStateChanges() and Observable emitted by RxBleConnection.setupNotification() / RxBleConnection.setupIndication()
  2. One value — these usually are meant for auto cleanup upon disposing i.e. setupNotification() / setupIndication() — when you will dispose the notification / indication will be disabled

RxBleDevice.establishConnection() is an Observable that will emit a single RxBleConnection but will not complete as the connection may be later a subject to an error (i.e. external disconnection). Whenever you are no longer interested in keeping the connection open you should dispose it which will cause disconnection and cleanup of resources.

The below table contains an overview of used Observable patterns

Interface Function Number of values Hot/Cold
RxBleClient scanBleDevices()* Infinite cold
RxBleClient observeStateChanges() Infinite** hot
RxBleDevice observeConnectionStateChanges() Infinite hot
RxBleDevice establishConnection()* One cold
RxBleConnection setupNotification()* One cold
RxBleConnection setupNotification() emitted Observable Infinite** hot
RxBleConnection setupIndication()* One cold
RxBleConnection setupIndication() emitted Observable Infinite** hot
RxBleConnection queue() User defined cold

* this Observable when disposed closes/cleans up internal resources (i.e. finishes scan, closes a connection, disables notifications)
** this Observable may complete. For example observeStateChanges() does emit only a single value and finishes in exactly one situation — when Bluetooth Adapter is not available on the device. There is no reason to monitor other states as the adapter does not appear during runtime. A second example: Observables emitted from setupNotification / setupIndication may complete when the parent Observable is disposed.

Helpers

We encourage you to check the package com.polidea.rxandroidble2.helpers and com.polidea.rxandroidble2.utils which contain handy reactive wrappers for some typical use-cases.

Value interpretation

Bluetooth Specification specifies formats in which int/float/String values may be stored in characteristics. BluetoothGattCharacteristic has functions for retrieving those (.getIntValue()/.getFloatValue()/.getStringValue()). Since RxAndroidBle reads and notifications emit byte[] you may want to use ValueIntepreter helper to retrieve the same data easily.

Observing BluetoothAdapter state

If you would like to observe BluetoothAdapter state changes you can use RxBleAdapterStateObservable.

More examples

Usage examples are located in:

Keep in mind that these are only samples to show how the library can be used. These are not meant for being role model of a good application architecture.

Testing

Using RxAndroidBle enables you to test your application easily.

Unit tests

Most of the objects that the library uses are implementations of interfaces which can be mocked.

Alternatively one could use MockRxAndroidBle (more info below). Note: Using MockRxAndroidBle in unit tests needs Robolectric.

Integration tests

Sometimes there is a need to develop the application without the access to a physical device. We have created MockRxAndroidBle as a drop-in addon for mocking a simple peripheral.

Unfortunately it is not under active development—PRs are welcomed though. ;)

Contributing

If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request.

When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. Please also make sure your code compiles by running ./gradlew clean checkstyle test.

FAQ

If you encounter seemingly incorrect behaviour in your application that is regarding this library please check the below list of Frequently Asked Questions:

Support

Discussions

Want to talk about it? Join our discussion on Gitter

Maintainers

  • Dariusz Seweryn (github: dariuszseweryn)

Contributors, thank you!

License

Copyright 2016 Polidea Sp. z o.o
Copyright 2021 Dariusz Seweryn 

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

rxandroidble's People

Contributors

ardovic avatar bharathmg avatar bleeding182 avatar bntnam avatar dariuszseweryn avatar ened avatar filippodelfra avatar fracturedpsyche avatar ivanph avatar kaiaai avatar lukaszkalnik avatar maoueh avatar mapyo avatar martinsadovy avatar martiwi avatar mechatronik avatar mikolak avatar mzgreen avatar nightscape avatar nrbrook avatar passsy avatar piotrdubiel avatar piotrek1543 avatar roman-upnext avatar sebirdman avatar seotrader avatar theosotr avatar tomaszmnich avatar ukl avatar ykc415 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rxandroidble's Issues

Make RxBleConnectionState compile time constant, e.g. an enum. Otherwise I get "Constant expression required".

RxBleConnectionState can't be used in the switch-case statement because they are not compile time constant, see http://stackoverflow.com/questions/3827393/java-switch-statement-constant-expression-required-but-it-is-constant. Maybe make RxBleConnectionState an enum?

For example this is illegal:

import static com.polidea.rxandroidble.RxBleConnection.RxBleConnectionState.CONNECTED;

switch(connectionState){
    case CONNECTED:
        break;
    default:
        break;
}

because Android Studio complaints Constant expression required.

Unable to add service to advertisement payload using the mock RxBleClient/RxBleDevice

Summary

Using the MockRxAndroidBle class to mock bluetooth devices, I am unable to make an added service show in the advertisement payload.

Steps to reproduce actual result

  1. Create a mock RxBleDevice with a name, mac address, rssi, and a service with a given UUID
  2. Add that device to a mock RxBleClient
  3. Scan for that device by the UUID: rxBleClient.scanDevices(UUID)

Actual result

no devices are found

Expected result

The device that was added to the mock RxBleClient is found. This works as expected if I call rxBleClient.scanDevices() with no UUID. So I believe the issue is the service I am adding is not being used in the advertisement payload, is there any way to accomplish this? Thanks you!

Writing to a characteristic and getting a response on a Notification Characteristic

Summary

First of all, thank you for this awesome library. I am currently working on a BLE project where to do read requests I need to write certain bytes on a write-only characteristic, the hardware module processes the request then answers back on a notification-only characteristic. How would you suggest that I proceed to Write -> Get response in these conditions using RxAndroidBle? I want to avoid writing multiple times then getting de-synced responses since I am working with 2 different characteristics.

I need to wait for response because my next write will usually depend on the type of response I get back.

Crashing setUpNotification IllegalStateException

Summary


after connecting to a device then enabling the notifications #### Preconditions
BLE ON #### Steps to reproduce actual result 1. Connect device 2. Setup notification #### Actual result
FATAL EXCEPTION: RxNewThreadScheduler-2339
Process: jp.co.qoled.furost, PID: 13127
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    at java.lang.Thread.run(Thread.java:818)
 Caused by: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@6260155 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@91c340c[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 1]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2014)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:794)
    at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:302)
    at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:527)
    at java.util.concurrent.ScheduledThreadPoolExecutor.submit(ScheduledThreadPoolExecutor.java:626)
    at rx.internal.schedulers.NewThreadWorker.scheduleActual(NewThreadWorker.java:239)
    at rx.internal.schedulers.NewThreadWorker.schedule(NewThreadWorker.java:224)
    at rx.internal.schedulers.NewThreadWorker.schedule(NewThreadWorker.java:216)
    at rx.internal.operators.OperatorSubscribeOn$1$1$1.request(OperatorSubscribeOn.java:82)
    at rx.Subscriber.request(Subscriber.java:157)
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:225)
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
    at java.lang.Thread.run(Thread.java:818) 

Expected result


Crashing app.

Subscription to observeConnectionStateChanges is triggered before establishConnection

I subscribe to both:

mCompositeSubscription.add(rxBleDevice.establishConnection(context, false)
    .subscribe(new ConnSubscriber()));
mCompositeSubscription.add(rxBleDevice.observeConnectionStateChanges()
    .subscribe(new ConnStateSubscriber())); 

The in logcat I see:

08-08 09:23:58.473 30099-30111/com.test.app D/BluetoothGatt: onClientRegistered() - status=0 clientIf=5
08-08 09:24:06.595 30099-30166/com.test.app D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=XX:XX:XX:XX:XX:XX
08-08 09:24:06.596 30099-30166/com.test.app D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
08-08 09:24:06.619 30099-31316/com.test.app D/XXXConnStateSubscriber: Frodo => [@Subscriber :: XXXConnStateSubscriber -> onNext() -> RxBleConnectionState{CONNECTED} :: @ObserveOn -> RxNewThreadScheduler-1]
08-08 09:24:06.619 30099-31316/com.test.app V/XXXConnStateSubscriber: onNext: RxBleConnectionState{CONNECTED} thread Thread[RxNewThreadScheduler-1,5,main]
08-08 09:24:06.619 30099-31316/com.test.app V/XXXConnStateSubscriber: onError: java.lang.NullPointerException: Attempt to invoke interface method 'rx.Observable com.polidea.rxandroidble.RxBleConnection.setupNotification(java.util.UUID)' on a null object reference
08-08 09:24:06.632 30099-31316/com.test.app D/XXXConnStateSubscriber: Frodo => [@Subscriber :: XXXConnStateSubscriber -> @Received -> 2 elements :: @Time -> 8169 ms]
08-08 09:24:06.632 30099-31316/com.test.app D/XXXConnStateSubscriber: Frodo => [@Subscriber :: XXXConnStateSubscriber -> onError() -> java.lang.NullPointerException: Attempt to invoke interface method 'rx.Observable com.polidea.rxandroidble.RxBleConnection.setupNotification(java.util.UUID)' on a null object reference]
08-08 09:24:06.632 30099-31316/com.test.app V/XXXBleXX:XX:XX:XX:XX:XX: observeConnectionStateChanges doOnUnsubscribeXX:XX:XX:XX:XX:XX
08-08 09:24:06.636 30099-31316/com.test.app D/XXXConnSubscriber: Frodo => [@Subscriber :: XXXConnSubscriber -> onNext() -> com.polidea.rxandroidble.internal.connection.RxBleConnectionImpl@70ce04 :: @ObserveOn -> RxNewThreadScheduler-1]
08-08 09:24:06.636 30099-31316/com.test.app V/XXXConnSubscriber: onNext: com.polidea.rxandroidble.internal.connection.RxBleConnectionImpl@70ce04 thread Thread[RxNewThreadScheduler-1,5,main]
08-08 09:24:06.637 30099-30127/com.test.app D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(192961747)

So XXXConnStateSubscriber: onNext is called BEFORE XXXConnSubscriber: onNext. Does it make sense to have it in this sequence? Why to report "connected" when connection instance could not (yet) be delivered to the application?

The java.lang.NullPointerException is thrown because I try to make another call to BLE (establish characteristic notification) on connection which is still null.

Location permission

I am just curious, why the library requires to have location permissions enabled in order to work?

Thanks!

Can't re-establish connection

Some times phone lost connection, and it isn't establish connection to device again


#### Preconditions

Maybe I do something wrong

Steps to reproduce actual result

  1. connect ble device
  2. turn off ble device(connection lost)
  3. turn on ble device

Actual result

connection establishment doesn't work, disconnected called immediately after establish call

Expected result

connection established again


Source code:
RxBleDevice rxBleDevice = bleClient.getBleDevice(pairedDevice.getMac());
compositeSubscription.add(rxBleDevice.establishConnection(context, true)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(connectionSubscriber));
I just call this code again when connection state change(disconnected) appear.

Please clarify wake locks

Your documentation says

It also handles acquiring wakelockes, so it's safe to assume that your Android device will be woken up after the connection has been established.

but it's quite important thing and how RxAndroidBle works with wake locks needs to be documented.

For example this code seems NOT to be wake-lock safe (I try to auto reconnect with 5 seconds delay using retryWhen):

rxBleDevice.establishConnection(context, false) // <-- autoConnect flag
    .doOnError(throwable -> {
        Log.d(TAG, "rxBleConnection doOnError: " + throwable);
    })
    .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
        @Override
        public Observable<?> call(Observable<? extends Throwable> errorNotification) {
            return errorNotification
                    .flatMap(new Func1<Throwable, Observable<?>>() {
                        @Override
                        public Observable<?> call(Throwable throwable) {
                            Log.v(TAG, "connect retrying...");
                            return Observable.timer(5, TimeUnit.SECONDS);
                        }
                    });
        }
    })
    .subscribe(rxBleConnection -> {
        Log.d(TAG, "rxBleConnection: " + rxBleConnection);
    });

Scanning in background

Awesome library.

I'm curious as to how you guys recommend scanning when the app is backgrounded. Generally I'm scanning in a service that keeps running even after the app is backgrounded. I know in Android 5.0(?) they added a LOW_POWER_MODE or something to that effect for scanning and wondering if you guys leverage this? Or generally what your thoughts are on the best way to scan using RxAndroidBle while backgrounded. Thanks!

Suggested way of unsubscribing connection with multiple devices

Originally posted by @zzt93 in http://github.com/Polidea/RxAndroidBle/issues/29#issuecomment-224528530

  • What the best practice to unsubscribe connection if I want to connect to many devices and communicate ? in Activity#onDestory? or like the following code
Observable<RxBleConnection> connectionSrc = rxBleScanResult
        .getBleDevice()
        .establishConnection(activity, false);

// 6/8/16  when found the device, stop scanning
subscribe.unsubscribe();

// TODO: 6/8/16 when un-subscribe this
Subscription conSubscription = connectionSrc
        .flatMap(new Func1<RxBleConnection, Observable<byte[]>>() {
            @Override
            public Observable<byte[]> call(final RxBleConnection rxBleConnection) {
                return rxBleConnection.readCharacteristic(BLE.BLE_CHAT_UUID);
            }
        })
        .subscribe(new Action1<byte[]>() {
            @Override
            public void call(byte[] bytes) {
                // read bytes              
                **conSubscription.unsubscribe();**
            }
        });

BluetoothGatt can't close when device is try to connect.

Summary


Normal if device is connected,when exit activity or fragment,the bluetooth will close,but if the device is try to connect(the device is offline),the bluetooth not close right now,after maybe one or two minute later,the bluetooth close.

08-17 07:31:56.260 7477-7477/com.polidea.rxandroidble.sample D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(140952845)
08-17 07:31:56.262 7477-7498/com.polidea.rxandroidble.sample D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(140952845)
08-17 07:31:56.279 7477-7477/com.polidea.rxandroidble.sample V/RxBle#BleConnectionCompat: Connecting without reflection
08-17 07:31:56.281 7477-7477/com.polidea.rxandroidble.sample D/BluetoothGatt: connect() - device: C6:45:40:E8:BC:3F, auto: false
08-17 07:31:56.281 7477-7477/com.polidea.rxandroidble.sample D/BluetoothGatt: registerApp()
08-17 07:31:56.285 7477-7477/com.polidea.rxandroidble.sample D/BluetoothGatt: registerApp() - UUID=f0583ad9-c73c-4ed6-8324-e6e3ec359e83
08-17 07:31:56.287 7477-7489/com.polidea.rxandroidble.sample D/BluetoothGatt: onClientRegistered() - status=0 clientIf=5
08-17 07:32:00.024 7477-7477/com.polidea.rxandroidble.sample D/RxBle#Radio:   QUEUED RxBleRadioOperationDisconnect(266187038)
08-17 07:32:00.203 7477-7504/com.polidea.rxandroidble.sample E/Surface: getSlotFromBufferLocked: unknown buffer: 0x9c4fa050
08-17 07:32:00.354 7477-7477/com.polidea.rxandroidble.sample E/Sample: onDestory

08-17 07:32:26.335 7477-7488/com.polidea.rxandroidble.sample D/BluetoothGatt: onClientConnectionState() - status=133 clientIf=5 device=C6:45:40:E8:BC:3F
08-17 07:32:26.337 7477-7488/com.polidea.rxandroidble.sample D/RxBle#BluetoothGatt: onConnectionStateChange newState=0 status=133
08-17 07:32:26.358 7477-7498/com.polidea.rxandroidble.sample D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(140952845)
08-17 07:32:26.359 7477-7498/com.polidea.rxandroidble.sample D/RxBle#Radio:  STARTED RxBleRadioOperationDisconnect(266187038)
08-17 07:32:26.363 7477-7477/com.polidea.rxandroidble.sample D/BluetoothManager: getConnectionState()
08-17 07:32:26.363 7477-7477/com.polidea.rxandroidble.sample D/BluetoothManager: getConnectedDevices
08-17 07:32:26.368 7477-7498/com.polidea.rxandroidble.sample D/RxBle#Radio: FINISHED RxBleRadioOperationDisconnect(266187038)
08-17 07:32:26.368 7477-7477/com.polidea.rxandroidble.sample D/BluetoothGatt: close()
08-17 07:32:26.368 7477-7477/com.polidea.rxandroidble.sample D/BluetoothGatt: unregisterApp() - mClientIf=5

You will see BluetoothGatt do not close when onDestory immediately

Preconditions


The device is offline,try to connect it.

Steps to reproduce actual result

1.Connect a offline device
2.Exit activity

Actual result


The BluetoothGatt not close right now

Expected result


The BluetoothGatt close right now

Allow monitoring RxBleClient state with observable.

Summary

Currently there is no API to monitor BLE state with observables. On iOS there is a property which allows you to know if BLE was turned off, is unsupported etc. It would be nice to have an observable emitting such state changes which could be then chained with other functions (for example scanning).

It's a feature request.

Indications are not supported.

Summary

There doesn't appear to be a way to set up indication callbacks without first enabling notifications and then updating the descriptor. The particular device that I am interacting with doesn't appear to handle that well, leading to sporadic timeouts. A setupIndication(UUID characteristic) method would be helpful to have in addition to setupNoticication()

See my hack.

BleGattException CHARACTERISTIC_READ: Device disconnects but BluetoothGatt is not closed properly

I am currently using Bluez 4.51 and its D-Bus API to turn a Raspberry Pi 3 into a BLE peripheral. It provides a single service with a few characteristics to read from and write to.

The app uses the following code to connect to the periheral and to read some characteristics:

connectionObservable = bleDevice.establishConnection(this, false)
    .compose(bindUntilEvent(ActivityEvent.PAUSE))
    .doOnUnsubscribe(this::clearConnectionObservable)
    .unsubscribeOn(AndroidSchedulers.mainThread())
    .compose(new ConnectionSharingAdapter());

connectionObservable
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(this::onDeviceConnected, this::onConnectionFailure);

// In onDeviceConnected
connection
    .readCharacteristic(UUID.fromString(row_uuid))
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bytes -> setButtonColors(temp_row, bytes), this::onConnectionFailure);

As long as nothing unexpected happens, this works as intended. The app searches for the peripheral, connects to it, reads some characteristics and disconnects from it on the pause event.

08-27 20:27:28.389 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationScan(208722037)
08-27 20:27:28.390 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationScan(208722037)
08-27 20:27:28.396 9464-9512/de.tobiastrumm.bluetoothledmatrix D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
08-27 20:27:28.402 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: startLeScan(): null
08-27 20:27:28.404 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: STATE_ON
08-27 20:27:28.409 9464-9482/de.tobiastrumm.bluetoothledmatrix D/BluetoothLeScanner: onClientRegistered() - status=0 clientIf=5
08-27 20:27:28.412 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationScan(208722037)
08-27 20:27:28.483 9464-9512/de.tobiastrumm.bluetoothledmatrix I/Adreno: QUALCOMM build                   : 63c06b2, I8366cd0437
                                                                         Build Date                       : 12/06/15
                                                                         OpenGL ES Shader Compiler Version: XE031.05.13.02
                                                                         Local Branch                     : mybranch17112971
                                                                         Remote Branch                    : quic/LA.BF64.1.2.9_v2
                                                                         Remote Branch                    : NONE
                                                                         Reconstruct Branch               : NOTHING
08-27 20:27:28.489 9464-9512/de.tobiastrumm.bluetoothledmatrix I/OpenGLRenderer: Initialized EGL, version 1.4
08-27 20:27:32.754 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: stopLeScan()
08-27 20:27:32.757 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: STATE_ON
08-27 20:27:32.837 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(33739039)
08-27 20:27:32.838 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(33739039)
08-27 20:27:32.861 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: connect() - device: B8:27:EB:A7:1B:9D, auto: false
08-27 20:27:32.862 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: registerApp()
08-27 20:27:32.862 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: registerApp() - UUID=48690a14-deaa-4d00-9907-bb50ddeef349
08-27 20:27:32.866 9464-9482/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientRegistered() - status=0 clientIf=5
08-27 20:27:34.204 9464-9482/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=B8:27:EB:A7:1B:9D
08-27 20:27:34.225 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
08-27 20:27:34.254 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(33739039)
08-27 20:27:34.280 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationServicesDiscover(105210533)
08-27 20:27:34.282 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationServicesDiscover(105210533)
08-27 20:27:34.295 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: discoverServices() - device: B8:27:EB:A7:1B:9D
08-27 20:27:34.301 9464-9482/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onSearchComplete() = Device=B8:27:EB:A7:1B:9D Status=0
08-27 20:27:34.302 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onServicesDiscovered status=0
08-27 20:27:34.329 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(7225693)
08-27 20:27:34.332 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(123841184)
08-27 20:27:34.334 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(142559999)
08-27 20:27:34.336 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(43676202)
08-27 20:27:34.338 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(143636369)
08-27 20:27:34.341 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(20737380)
08-27 20:27:34.343 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(15134611)
08-27 20:27:34.345 9464-9664/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(218267342)
08-27 20:27:34.347 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationServicesDiscover(105210533)
08-27 20:27:34.348 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(7225693)
08-27 20:27:34.420 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0000 status=0
08-27 20:27:34.426 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(7225693)
08-27 20:27:34.427 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(123841184)
08-27 20:27:34.512 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0001 status=0
08-27 20:27:34.523 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(123841184)
08-27 20:27:34.525 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(142559999)
08-27 20:27:34.602 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0002 status=0
08-27 20:27:34.610 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(142559999)
08-27 20:27:34.612 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(43676202)
08-27 20:27:34.694 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0003 status=0
08-27 20:27:34.704 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(43676202)
08-27 20:27:34.706 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(143636369)
08-27 20:27:34.902 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0004 status=0
08-27 20:27:34.911 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(143636369)
08-27 20:27:34.913 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(20737380)
08-27 20:27:35.024 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0005 status=0
08-27 20:27:35.032 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(20737380)
08-27 20:27:35.034 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(15134611)
08-27 20:27:35.141 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0006 status=0
08-27 20:27:35.149 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(15134611)
08-27 20:27:35.152 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(218267342)
08-27 20:27:35.324 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0007 status=0
08-27 20:27:35.333 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(218267342)
08-27 20:27:39.071 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationDisconnect(69216652)
08-27 20:27:39.072 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationDisconnect(69216652)
08-27 20:27:39.099 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothManager: getConnectionState()
08-27 20:27:39.099 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothManager: getConnectedDevices
08-27 20:27:39.124 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: cancelOpen() - device: B8:27:EB:A7:1B:9D
08-27 20:27:39.138 9464-9482/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=B8:27:EB:A7:1B:9D
08-27 20:27:39.139 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onConnectionStateChange newState=0 status=0
08-27 20:27:39.145 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationDisconnect(69216652)
08-27 20:27:39.187 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: close()
08-27 20:27:39.188 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: unregisterApp() - mClientIf=5

The issue occurs when the program that provides the service over the D-Bus api on the Pi is terminated. Bluez then removes the service with its characteristics, but does not disconnect any connected central device. As expected, a BleGattException occurs if the app tries to read a characteristic that is no longer there.

08-27 20:28:19.809 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationScan(182191558)
08-27 20:28:19.810 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationScan(182191558)
08-27 20:28:19.814 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: startLeScan(): null
08-27 20:28:19.816 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: STATE_ON
08-27 20:28:19.819 9464-9482/de.tobiastrumm.bluetoothledmatrix D/BluetoothLeScanner: onClientRegistered() - status=0 clientIf=5
08-27 20:28:19.825 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationScan(182191558)
08-27 20:28:19.860 9464-9512/de.tobiastrumm.bluetoothledmatrix I/Adreno: QUALCOMM build                   : 63c06b2, I8366cd0437
                                                                         Build Date                       : 12/06/15
                                                                         OpenGL ES Shader Compiler Version: XE031.05.13.02
                                                                         Local Branch                     : mybranch17112971
                                                                         Remote Branch                    : quic/LA.BF64.1.2.9_v2
                                                                         Remote Branch                    : NONE
                                                                         Reconstruct Branch               : NOTHING
08-27 20:28:19.869 9464-9512/de.tobiastrumm.bluetoothledmatrix I/OpenGLRenderer: Initialized EGL, version 1.4
08-27 20:28:33.762 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: stopLeScan()
08-27 20:28:33.766 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: STATE_ON
08-27 20:28:33.785 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(185320209)
08-27 20:28:33.787 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(185320209)
08-27 20:28:33.808 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: connect() - device: B8:27:EB:A7:1B:9D, auto: false
08-27 20:28:33.809 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: registerApp()
08-27 20:28:33.810 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: registerApp() - UUID=1cf80b9c-c6e3-458d-88e6-c646acc724db
08-27 20:28:33.816 9464-9483/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientRegistered() - status=0 clientIf=5
08-27 20:28:35.228 9464-10674/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=B8:27:EB:A7:1B:9D
08-27 20:28:35.247 9464-10674/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
08-27 20:28:35.255 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(185320209)
08-27 20:28:35.255 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationServicesDiscover(152166182)
08-27 20:28:35.257 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationServicesDiscover(152166182)
08-27 20:28:35.269 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: discoverServices() - device: B8:27:EB:A7:1B:9D
08-27 20:28:35.275 9464-9482/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onSearchComplete() = Device=B8:27:EB:A7:1B:9D Status=0
08-27 20:28:35.276 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onServicesDiscovered status=0
08-27 20:28:35.289 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(108149855)
08-27 20:28:35.291 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(75602698)
08-27 20:28:35.294 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(140778993)
08-27 20:28:35.297 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(175591236)
08-27 20:28:35.299 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(264839923)
08-27 20:28:35.301 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(109793710)
08-27 20:28:35.303 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(158773221)
08-27 20:28:35.306 9464-11057/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(65777864)
08-27 20:28:35.307 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationServicesDiscover(152166182)
08-27 20:28:35.308 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(108149855)
08-27 20:28:35.445 9464-9483/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0000 status=0
08-27 20:28:35.457 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(108149855)
08-27 20:28:35.459 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(75602698)
08-27 20:28:35.533 9464-10674/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0001 status=0
08-27 20:28:35.540 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(75602698)
08-27 20:28:35.541 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(140778993)
08-27 20:28:35.621 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0002 status=0
08-27 20:28:35.626 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(140778993)
08-27 20:28:35.628 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(175591236)
08-27 20:28:35.715 9464-9483/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0003 status=0
08-27 20:28:35.723 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(175591236)
08-27 20:28:35.725 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(264839923)
08-27 20:28:35.863 9464-10674/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0004 status=0
08-27 20:28:35.869 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(264839923)
08-27 20:28:35.871 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(109793710)
08-27 20:28:35.983 9464-9482/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0005 status=0
08-27 20:28:35.990 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(109793710)
08-27 20:28:35.992 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(158773221)
08-27 20:28:36.104 9464-9483/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0006 status=0
08-27 20:28:36.114 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(158773221)
08-27 20:28:36.115 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(65777864)
08-27 20:28:36.223 9464-10674/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0007 status=0
08-27 20:28:36.230 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(65777864)
08-27 20:29:15.162 9464-9464/de.tobiastrumm.bluetoothledmatrix D/LedControlActivity: Pressed button [0][0]
08-27 20:29:15.185 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(173308595)
08-27 20:29:15.187 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(173308595)
08-27 20:29:15.287 9464-10674/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0000 status=1
08-27 20:29:15.293 9464-9464/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationDisconnect(138501230)
08-27 20:29:15.293 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(173308595)
08-27 20:29:15.294 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationDisconnect(138501230)
08-27 20:29:15.367 9464-9464/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                         at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                         at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                         at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                         at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                         at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                         at android.os.Binder.execTransact(Binder.java:453)
08-27 20:29:15.370 9464-9464/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                         at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                         at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                         at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                         at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                         at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                         at android.os.Binder.execTransact(Binder.java:453)
08-27 20:29:15.371 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothManager: getConnectionState()
08-27 20:29:15.371 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothManager: getConnectedDevices
08-27 20:29:15.374 9464-9464/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: cancelOpen() - device: B8:27:EB:A7:1B:9D
08-27 20:29:15.377 9464-9507/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationDisconnect(138501230)
08-27 20:29:15.381 9464-10674/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=B8:27:EB:A7:1B:9D
08-27 20:29:15.382 9464-10674/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onConnectionStateChange newState=0 status=0
08-27 20:29:15.505 9464-9512/de.tobiastrumm.bluetoothledmatrix V/RenderScript: 0x7f8a6b2000 Launching thread(s), CPUs 6

As you can see, RxBleRadioOperationDisconnect is started and finishes. Using bluetoothctl on the Pi i can see, that the Android device is being disconnected, but Logcat never shows that BluetoothGatt was closed.

The BleGattExceptions still occur even after the service and its characteristics are added to the peripheral again.

08-27 20:30:27.835 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: stopLeScan()
08-27 20:30:27.840 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothAdapter: STATE_ON
08-27 20:30:27.932 12770-12770/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(33739039)
08-27 20:30:27.940 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(33739039)
08-27 20:30:27.949 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: connect() - device: B8:27:EB:A7:1B:9D, auto: false
08-27 20:30:27.949 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: registerApp()
08-27 20:30:27.950 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: registerApp() - UUID=0036871f-09be-4403-94cf-7d459063e7d4
08-27 20:30:27.952 12770-12781/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientRegistered() - status=0 clientIf=5
08-27 20:30:30.516 12770-12781/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=B8:27:EB:A7:1B:9D
08-27 20:30:30.518 12770-12781/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
08-27 20:30:30.543 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(33739039)
08-27 20:30:30.553 12770-12770/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationServicesDiscover(105210533)
08-27 20:30:30.555 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationServicesDiscover(105210533)
08-27 20:30:30.564 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: discoverServices() - device: B8:27:EB:A7:1B:9D
08-27 20:30:30.573 12770-12781/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onSearchComplete() = Device=B8:27:EB:A7:1B:9D Status=0
08-27 20:30:30.575 12770-12781/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onServicesDiscovered status=0
08-27 20:30:30.611 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(7225693)
08-27 20:30:30.614 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(123841184)
08-27 20:30:30.617 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(142559999)
08-27 20:30:30.621 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(43676202)
08-27 20:30:30.624 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(143636369)
08-27 20:30:30.627 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(20737380)
08-27 20:30:30.630 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(15134611)
08-27 20:30:30.632 12770-12838/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationCharacteristicRead(218267342)
08-27 20:30:30.634 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationServicesDiscover(105210533)
08-27 20:30:30.635 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(7225693)
08-27 20:30:30.735 12770-12781/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0000 status=1
08-27 20:30:30.744 12770-12781/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:   QUEUED RxBleRadioOperationDisconnect(223066586)
08-27 20:30:30.745 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(7225693)
08-27 20:30:30.747 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(123841184)
08-27 20:30:30.748 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.761 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.778 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(123841184)
08-27 20:30:30.779 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(142559999)
08-27 20:30:30.820 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.822 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(142559999)
08-27 20:30:30.823 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(43676202)
08-27 20:30:30.845 12770-12790/de.tobiastrumm.bluetoothledmatrix V/RenderScript: 0x7f8d3e8000 Launching thread(s), CPUs 6
08-27 20:30:30.873 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.876 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(43676202)
08-27 20:30:30.880 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(143636369)
08-27 20:30:30.889 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.891 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(143636369)
08-27 20:30:30.893 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(20737380)
08-27 20:30:30.903 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.905 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(20737380)
08-27 20:30:30.906 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(15134611)
08-27 20:30:30.919 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.921 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(15134611)
08-27 20:30:30.922 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationCharacteristicRead(218267342)
08-27 20:30:30.950 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.952 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationCharacteristicRead(218267342)
08-27 20:30:30.953 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio:  STARTED RxBleRadioOperationDisconnect(223066586)
08-27 20:30:30.956 12770-12781/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onCharacteristicRead characteristic=12345678-1234-5678-1234-56789abc0001 status=1
08-27 20:30:30.994 12770-12770/de.tobiastrumm.bluetoothledmatrix E/LedControlActivity: BleGattException{status=1, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
                                                                                           at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicRead(RxBleGattCallback.java:94)
                                                                                           at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:356)
                                                                                           at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:249)
                                                                                           at android.os.Binder.execTransact(Binder.java:453)
08-27 20:30:30.996 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothManager: getConnectionState()
08-27 20:30:30.996 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothManager: getConnectedDevices
08-27 20:30:30.999 12770-12770/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: cancelOpen() - device: B8:27:EB:A7:1B:9D
08-27 20:30:31.002 12770-12785/de.tobiastrumm.bluetoothledmatrix D/RxBle#Radio: FINISHED RxBleRadioOperationDisconnect(223066586)
08-27 20:30:31.004 12770-12781/de.tobiastrumm.bluetoothledmatrix D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=B8:27:EB:A7:1B:9D
08-27 20:30:31.005 12770-12781/de.tobiastrumm.bluetoothledmatrix D/RxBle#BluetoothGatt: onConnectionStateChange newState=0 status=0

The reason for that could be that Bluez does not assign the same handles to services and characteristics if they are removed and added again. But because BluetoothGatt was not closed, Android still expects the characteristics to be reachable under the old handles, I guess.

The only way I could get the app running again was to disable and then re-enable bluetooth on the Android device.

Modify behaviour of `.observeConnectionStateChanges()`

I'd like to react in my app when RxBle#Radio: FINISHED happened - because for example I'd like to inform my UI when device actually finshed disconnecting.

Now, I believe, I can only unsubscribe from subscription on RxBleDevice#establishConnection and I don't know what happened later.

mockrxandroidble not available on gradle

Summary

Unable to install mockrxandroidble from gradle

Preconditions

Using jcenter repository

Steps to reproduce actual result

  1. add `compile 'com.polidea.rxandroidble:mockrxandroidble:1.0.1'` in build.gradle
    
  2. Hit Sync
  3. Error shows up saying it's failing to sync

Actual result

Error:Could not find com.polidea.rxandroidble:mockrxandroidble:1.0.1.

Expected result

Should install mockrxandroidble

Description for "Auto connect" is misleading

As it highly depends on phone manufacturer, description of Auto connect makes more harm than it helps (it may be true on some devices and it may not be true on some devices).

Auto connect

Auto connect concept may be misleading at first glance. Without the autoconnect flag the connection will end up with an error if a BLE device is not advertising when the RxBleDevice#establishConnection method is called. From platform to platform timeout after which the error is emitted differs, but in general it is rather tens of seconds than single seconds.

Setting the auto connect flag allows you to wait until the BLE device becomes discoverable. The RxBleConnection instance won't be emited until the connection is fully set up. It also handles acquiring wakelockes, so it's safe to assume that your Android device will be woken up after the connection has been established.

Be careful not to overuse the autoconnect flag. On the other side it has negative impact on the connection initialization speed. Scanning window and interval is lowered as it is optimized for background use and depending on Bluetooth parameters it may take more time to establish the connection.

When I write data to the device, the device is automatically disconnected.

When I write data to the device, the data is written to the success.But the Bluetooth device will automatically disconnect.The error that is displayed in Samsung S6 is "BleGattException{status=10, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_WRITE'}}",but in other mobile phones, it is " BleGattException{status=133, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_WRITE'}} ".I want to know how to solve it.I'm running an example.thanks

Workaround for hung service discovery Android bug

I try to set up notifications just like you propose:

RxBleDevice rxBleDevice = rxBleClient.getBleDevice(address);
rxBleDevice.establishConnection(this, false)
        .flatMap(rxBleConnection -> rxBleConnection.setupNotification(BATTERY_LEVEL_UUID))
        .doOnNext(notificationObservable -> {
            // Notification has been set up
        })
        .flatMap(notificationObservable -> notificationObservable) // <-- Notification has been set up, now observe value changes.
        .subscribe(bytes -> {
            // Given characteristic has been changes, here is the value.
            Log.d(TAG, "bytes" + bytes.toString());
        });

And it does not work - I see in logcat:

D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(19575723)
D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(19575723)
D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
D/RxBle#Radio:   QUEUED RxBleRadioOperationServicesDiscover(175585553)
D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(19575723)
D/RxBle#Radio:  STARTED RxBleRadioOperationServicesDiscover(175585553)

STARTED RxBleRadioOperationServicesDiscover is the last log, then nothing is logged.

Suggested way of using connection

Originally posted by @zzt93 in #29 (comment)

  • read write combined vs simple read then simple write
    The following code is blog's code for read/write combined.
device.establishConnection(context, false)
    .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid)
        .doOnNext(bytes -> {
            // Process read data.
        })
        .flatMap(bytes -> rxBleConnection.writeCharacteristic(characteristicUuid, bytesToWrite))
     ).subscribe(writeBytes -> {
        // Written data.
    });

What is the difference between the following code:

Observable<RxBleConnection> connectionSrc = device.establishConnection(activity, false);             
connectionSrc
    .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
    .subscribe(characteristicValue -> {
        // Read characteristic value.
    });
connectionSrc
    .flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, bytesToWrite))
    .subscribe(characteristicValue -> {
        // Characteristic value confirmed.
    });

Automatic disconnect after enabling notifications

Summary

After connecting to a device then enabling the notifications, the device becomes disconnected automatically, how do I keep the device connected?

Preconditions

Device connected and notifications registered (I used the "CharacteristicOperationExampleActivity" provides with the samples)

Steps to reproduce actual result

  1. Connect to a device
  2. Enable notifications
  3. Wait 10 seconds, then the device will be disconnected and will need to reconnect manually

Actual result

D/BluetoothGatt: onClientConnectionState() - status=8 clientIf=5 device=C4:xx:xx:xx:0A:9F
D/RxBle#BluetoothGatt: onConnectionStateChange newState=0 status=8
D/RxBle#Radio: QUEUED RxBleRadioOperationDisconnect(785202367)
D/RxBle#Radio: STARTED RxBleRadioOperationDisconnect(785202367)
D/BluetoothGatt: setCharacteristicNotification() - uuid: 713d0002-503e-4c75-ba94-3148f18d941e enable: false
D/RxBle#Radio: QUEUED RxBleRadioOperationDescriptorWrite(433845284)
D/BluetoothManager: getConnectionState()
D/BluetoothManager: getConnectedDevices
D/RxBle#Radio: FINISHED RxBleRadioOperationDisconnect(785202367)
D/RxBle#Radio: STARTED RxBleRadioOperationDescriptorWrite(433845284)
D/log: Notifications error: BleGattException{status=8, bleGattOperation=BleGattOperation{description='CONNECTION_STATE'}}
D/BluetoothGatt: close()
D/BluetoothGatt: unregisterApp() - mClientIf=5
D/RxBle#Radio: FINISHED RxBleRadioOperationDescriptorWrite(433845284)

Expected result

Connection is persisted and no need to reconnect.

Scan stops briefly after stop

Summary

Another issue that I have noticed is that when perfoming scan, sometimes scan just drops. I checked this by hooking with doOnUnsubscribe and noticed it gets called really quickly (few seconds) after starting scan.

Steps to reproduce

If I have an already open connection with autoConnect flag, then when I start scan it gets unsubscribed really quickly. Functionality I need is that I want to remove device (stop the open connection) and then re-add it by scanning.

Kind Regards

Please add possibility to use hidden metod refresh on BluetoothGatt

Please add possibility to use hidden metod refresh on BluetoothGatt https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/bluetooth/BluetoothGatt.java:

/**
     * Clears the internal cache and forces a refresh of the services from the
     * remote device.
     * @hide
     */
    public boolean refresh() {
        if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
        if (mService == null || mClientIf == 0) return false;
        try {
            mService.refreshDevice(mClientIf, mDevice.getAddress());
        } catch (RemoteException e) {
            Log.e(TAG,"",e);
            return false;
        }
        return true;
    }

Connection will be closed after catching an Error with onErrorResumeNext

Summary


On each error the connection get closed. If i will catch an Error with onErrorResumeNext or something, the Connection also get closed.

Preconditions


In some cases, it makes no sense to close the connection.

e.g. BleGattException{status=137, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_READ'}}.

On some devices it is necessary to retry the action without disconnect.

Indicate not working

I am attempting to establish a connection to a device that is already bonded to my tablet.

If I just attempt to connect the bonded device I get an error with status 133 after 30 seconds.

If the device is in pairing mode and I can connect fine, but once the device stops advertising the connection is terminated and any further indications are lost.

If I set the device up to be attempting to broadcast data and then connect, the system errors with a status 19 right after discover Services, and the indications are never sent.

Thoughts on this? This does not happen with my own home grown BLE framework. I am evaluating yours as a replacement to mine but cannot get this blood pressure cuff to work.

Thanks!

Possible concurrency issue in scan

If we unsubscribe the scan right after subscribe (e.g., repeated activity pause/resume, or timeout/retryWhen), scanning started after stop request. We have to kill the app to stop it.

Here's a brief logcat output for the scenario.

// Subscribe and scan start

D/AObserver(31609): start scan
D/RxBle#Radio(31609):   QUEUED RxBleRadioOperationScan(7307165)
D/RxBle#Radio(31609):  STARTED RxBleRadioOperationScan(7307165)

// RxBleRadioImpl thread is running but startLeScan() is not called yet
// now we pause the activity and unsubscribe

V/AActivity(31609): ⇢ onPause()
D/AObserver(31609): Frodo => [@Observable#observe -> @SubscribeOn -> main :: @ObserveOn -> main]
D/AObserver(31609): Frodo => [@Observable#observe -> onUnsubscribe()]
D/BluetoothAdapter(31609): stopLeScan()
D/BluetoothAdapter(31609): scan not started yet    <------------------!!!!!!!!!!!
V/AActivity(31609): ⇠ onPause()

// RxBleRadioImpl thread is still running in the background
// and start the scan

D/BluetoothAdapter(31609): startLeScan(): null
D/BtGatt.GattService(13895): registerClient() - UUID=f17ebcd4-...
D/BtGatt.GattService(13895): onClientRegistered() - UUID=f17ebcd4-..., clientIf=5
D/BluetoothLeScanner(31609): onClientRegistered() - status=0 clientIf=5
D/BtGatt.GattService(13895): start scan with filters
D/BtGatt.ScanManager(13895): handling starting scan
D/BtGatt.ScanManager(13895): configureRegularScanParams() - queue=1
D/BtGatt.ScanManager(13895): configureRegularScanParams() - ScanSetting Scan mode=2 ...
D/RxBle#Radio(31609): FINISHED RxBleRadioOperationScan(7307165)
D/BluetoothLeScanner(31609): onScanResult() - ScanResult{mDevice= ...}
D/BluetoothLeScanner(31609): onScanResult() - ScanResult{mDevice= ...}
D/BluetoothLeScanner(31609): onScanResult() - ScanResult{mDevice= ...}
......

// there's no way to stop the scan. we have to kill the app

RxBleRadioOperation will never FINISHED

Summary

When I turn off Bluetooth,RxBleRadioOperation will never FINISHED.

Preconditions

turn off Bluetooth

Steps to reproduce actual result

1.turn off Bluetooth
2.call

connectionObservable = bleDevice
                .establishConnection(context, false)
                .takeUntil(disconnectTriggerSubject)
                .doOnUnsubscribe(BasicBle.this::clearSubscription)
                .compose(new ConnectionSharingAdapter());

3.log throw error

04-29 11:42:41.589 15014-15289/com.rokyinfo.rkbluetoothle_simple D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(237177820)
04-29 11:42:41.594 15014-15287/com.rokyinfo.rkbluetoothle_simple D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(237177820)
04-29 11:42:41.618 15014-15014/com.rokyinfo.rkbluetoothle_simple D/BluetoothGatt: connect() - device: C0:27:15:09:A7:E6, auto: false
04-29 11:42:41.618 15014-15014/com.rokyinfo.rkbluetoothle_simple D/BluetoothGatt: registerApp()
04-29 11:42:41.619 15014-15014/com.rokyinfo.rkbluetoothle_simple D/BluetoothGatt: registerApp() - UUID=0a08fd3e-9dd9-4353-8a9d-597912724c34
04-29 11:42:41.621 15014-15014/com.rokyinfo.rkbluetoothle_simple E/BluetoothGatt: android.os.DeadObjectException
                                                                                      at android.os.BinderProxy.transactNative(Native Method)
                                                                                      at android.os.BinderProxy.transact(Binder.java:509)
                                                                                      at android.bluetooth.IBluetoothGatt$Stub$Proxy.registerClient(IBluetoothGatt.java:851)
                                                                                      at android.bluetooth.BluetoothGatt.registerApp(BluetoothGatt.java:752)
                                                                                      at android.bluetooth.BluetoothGatt.connect(BluetoothGatt.java:808)
                                                                                      at android.bluetooth.BluetoothDevice.connectGatt(BluetoothDevice.java:1519)
                                                                                      at android.bluetooth.BluetoothDevice.connectGatt(BluetoothDevice.java:1487)
                                                                                      at com.polidea.rxandroidble.internal.util.BleConnectionCompat.connectGattCompat(BleConnectionCompat.java:85)
                                                                                      at com.polidea.rxandroidble.internal.util.BleConnectionCompat.connectGatt(BleConnectionCompat.java:35)
                                                                                      at com.polidea.rxandroidble.internal.operations.RxBleRadioOperationConnect.connect(RxBleRadioOperationConnect.java:78)
                                                                                      at com.polidea.rxandroidble.internal.operations.RxBleRadioOperationConnect.run(RxBleRadioOperationConnect.java:67)
                                                                                      at com.polidea.rxandroidble.internal.radio.RxBleRadioImpl$$Lambda$4.call(Unknown Source)
                                                                                      at rx.Observable$27.onNext(Observable.java:7928)
                                                                                      at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139)
                                                                                      at rx.internal.util.ScalarSynchronousObservable$ScalarSynchronousAction.call(ScalarSynchronousObservable.java:115)
                                                                                      at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
                                                                                      at android.os.Handler.handleCallback(Handler.java:815)
                                                                                      at android.os.Handler.dispatchMessage(Handler.java:104)
                                                                                      at android.os.Looper.loop(Looper.java:194)
                                                                                      at android.app.ActivityThread.main(ActivityThread.java:5779)
                                                                                      at java.lang.reflect.Method.invoke(Native Method)
                                                                                      at java.lang.reflect.Method.invoke(Method.java:372)
                                                                                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1004)
                                                                                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:799)
04-29 11:42:41.621 15014-15014/com.rokyinfo.rkbluetoothle_simple E/BluetoothGatt: Failed to register callback

Actual result

Error can not be thrown out
then queue will never release
can not step to

log("FINISHED", rxBleRadioOperation);

#### Expected result

throw the exception


Thank you very much to share, the library is great. use Chinese I will say "大牛"

Unhandled exception in callback

On Samsung Galaxy S5 I make:

rxBleDevice.establishConnection(context, false) // <-- autoConnect flag
    .doOnError(throwable -> {
        Log.d(TAG, "rxBleConnection doOnError: " + throwable);
    })
    .subscribe(rxBleConnection -> {
        Log.d(TAG, "rxBleConnection: " + rxBleConnection);
    });

rxBleDevice.observeConnectionStateChanges()
    .doOnError(throwable -> {
        Log.d(TAG, "connectionState doOnError: " + throwable);
    })
    .subscribe(connectionState -> {
        Log.d(TAG, "connectionState: " + connectionState);
    });

And I successfully connect to the device, then I remove battery form it and it should disconnect. I see in logs

BluetoothGatt: onClientConnectionState() - status=8 clientIf=8 device=XX:XX:XX:XX:XX:XX
...
W/BluetoothGatt: Unhandled exception in callback
    rx.exceptions.OnErrorNotImplementedException
    at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:386)
    at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:383)
    at rx.internal.util.ActionSubscriber.onError(ActionSubscriber.java:44)
    at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:157)
    at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:120)
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:72)
    at rx.observers.Subscribers$5.onError(Subscribers.java:230)
    at rx.observers.Subscribers$5.onError(Subscribers.java:230)
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:72)
    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:268)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:812)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:573)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:562)
    at rx.internal.operators.OperatorMerge$InnerSubscriber.onError(OperatorMerge.java:846)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:268)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:812)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:573)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:562)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.onError(OperatorMerge.java:278)
    at rx.internal.operators.OperatorMap$MapSubscriber.onError(OperatorMap.java:85)
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:72)
    at rx.internal.operators.OperatorFilter$FilterSubscriber.onError(OperatorFilter.java:87)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:268)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:812)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:573)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:562)
    at rx.internal.operators.OperatorMerge$InnerSubscriber.onError(OperatorMerge.java:846)
    at rx.internal.operators.NotificationLite.accept(NotificationLite.java:148)
    at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitNext(SubjectSubscriptionManager.java:255)
    at rx.subjects.BehaviorSubject.onError(BehaviorSubject.java:141)
    at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
    at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
    at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onConnectionStateChange(RxBleGattCallback.java:62)
    at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:182)
    at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
    at android.os.Binder.execTransact(Binder.java:446)
    Caused by: BleGattException{status=8, bleGattOperation=BleGattOperation{description='CONNECTION_STATE'}}
    at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245) 
    at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26) 
    at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onConnectionStateChange(RxBleGattCallback.java:62) 
    at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:182) 
    at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70) 
    at android.os.Binder.execTransact(Binder.java:446) 

ConnectionAlreadyEstablished Exception

If the connection for some reason already exists and method .establishConnection(context, true) is performed, I get an exception ConnectionAlreadyEstablished. Which strategy recovery state of connectionObservable in this case?

Support for API level 21 (package android.bluetooth.le.*)

As BluetoothAdapter#startScan has been deprecated, it would be interesting to support the calls from the mentioned package, which allow more control over the scanning process.

As a temporary hack, I created (copy-pasted, in fact) a scan operation class that uses the new API. It can be found here. My idea is that the scan operation receives ScanFilter and ScanSettings and emits ScanResult. All these classes are from the android.bluetooth.le package.

Needing thread hopping?

As far as I understand the asynchronous event, the observer's action will always be executed in another thread, right? So If I want to update ui, I have to use method like activity.runOnUiThread(), right?

.subscribe(new Action1<byte[]>() {
    @Override
    public void call(final byte[] bytes) {
        activity.runOnUiThread(
                new Runnable() {
                    @Override
                    public void run() {
                        // update ui
                    }
                }
        );
    }
});

Discovery service fails after bluetooth turn off/on

Summary

My current situation is that I have connected to device with autoconnect flag set. Which means I am waiting for the device to appear in order to connect. When I turn off/on bluetooth I establish again the connection. When the device becomes visible it connects but it hangs on discover services. I have done some debugging with nrf Connect and discover services returns true. Also I debugged RxAndroidBle and RxBleRadioOperationServicesDiscover operation returns true from bluetoothGatt.discoverServices(). However onServicesDiscovered is never called inside RxBleGattCallback. If I clear the app and open again then it immediatelly connects. I am still looking what might be wrong but no solution so far.

Preconditions

Connect with auto connect flag needs to be established.

Steps to reproduce actual result

  1. Connect with auto connect flag
  2. Turn airplane mode on
  3. Turn airplane mode off
  4. Connect with auto connect flag (make sure device is advertising)

Actual result

Discover services hangs

Expected result

Connection proceeds with onServicesDiscovered callback fired

Do you maybe have any ideas what I might be doing wrong?

Thanks!

Mocked client disconnects instantly

Summary

A mocked RxBleClient disconnects automatically right after a connection has been made.

Preconditions

Using "com.polidea.rxandroidble:mockclient:1.0.2"

Steps to reproduce actual result

  1. Create a RxBleClient mock, as shown in the example.
  2. Connect to a device using the mocked client.

Actual result

08-23 16:01:20.738 D/test: RxBleConnectionState{DISCONNECTED}
08-23 16:01:20.743 D/test: RxBleConnectionState{CONNECTING}
08-23 16:01:20.743 D/test: RxBleConnectionState{CONNECTED}
08-23 16:01:20.743 D/test: RxBleConnectionState{DISCONNECTED}

#### Expected result
08-23 16:01:20.738 D/test: RxBleConnectionState{DISCONNECTED}
08-23 16:01:20.743 D/test: RxBleConnectionState{CONNECTING}
08-23 16:01:20.743 D/test: RxBleConnectionState{CONNECTED}

#### Code for reproducing the problem
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//        RxBleClient rxBleClient = RxBleClient.create(this);
        RxBleClient  rxBleClient = setupMock();
        String macAddress = "D4:31:1D:1F:2F:97";
        RxBleDevice device = rxBleClient.getBleDevice(macAddress);

        device.observeConnectionStateChanges()
                .subscribe(connectionState -> {
                    Log.d("test", connectionState.toString());
                });
        Subscription subscription = device.establishConnection(this, false).subscribe(rxBleConnection -> {});
    }


    public RxBleClient setupMock() {
        return new RxBleClientMock.Builder()
                .addDevice(new RxBleClientMock.DeviceBuilder()
                        .deviceMacAddress("D4:31:1D:1F:2F:97")
                        .deviceName("Test")
                        .scanRecord(testScanRecordBytes())
                        .rssi(56)
                        .addService(
                                UUID.fromString("e7d713f0-a65b-4d1b-9fce-300c6ad8f751"),
                                new RxBleClientMock.CharacteristicsBuilder()
                                        .addCharacteristic(
                                                UUID.fromString("e7d73a7e-a65b-4d1b-9fce-300c6ad8f751"),
                                                new byte[] {2,3},
                                                new RxBleClientMock.DescriptorsBuilder().build()
                                        ).build()
                        ).build()).build();
    }

    private static byte[] testScanRecordBytes() {//same byte[] that was retrieved from a real scan of the device previously}
}

Simply uncomment the line RxBleClient rxBleClient = RxBleClient.create(this); and it works and stays connected to the real device "D4:31:1D:1F:2F:97".

Usage of BleConnectionCompat.connectUsingReflection() causes connection being never established on Sony Xperia Z (C6603)

Summary

A connection cannot be established using BleConnectionCompat.connectUsingReflection() (autoConnect=true ) on Sony Xperia Z (C6603) Android 5.1.1. Subscribe action is never called.
When I modified the library source code so that connectGattCompat(...) is always used the connection is established successfully.

Preconditions

autoConnect=true
Sony Xperia Z (C6603) Android 5.1.1

Steps to reproduce actual result

RxBleDevice device = rxBleClient.getBleDevice(macAddress);
Subscription subscription = device.establishConnection(context, true) // <-- autoConnect flag
                .subscribe(rxBleConnection -> {
                    Log.d(TAG, "Connection established");
                });

Actual result

Log.d(TAG, "Connection established"); is never called

Logs:

08-20 14:59:18.333 27298-27298/bletest D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(185299140)
08-20 14:59:18.334 27298-27888/bletest D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(185299140)
08-20 14:59:18.347 27298-27298/bletest V/RxBle#RadioOperationConnect: Trying to connectGatt using reflection.
08-20 14:59:18.348 27298-27298/bletest V/RxBle#BleConnectionCompat: Found constructor with args count = 4
08-20 14:59:18.349 27298-27298/bletest V/RxBle#BleConnectionCompat: Connecting using reflection
08-20 14:59:18.349 27298-27298/bletest D/BluetoothGatt: connect() - device: 6F:14:AB:E7:6D:01, auto: true
08-20 14:59:18.349 27298-27298/bletest D/BluetoothGatt: registerApp()
08-20 14:59:18.349 27298-27298/bletest D/BluetoothGatt: registerApp() - UUID=ac648d82-77cf-4068-9fcf-3753edb45e28
08-20 14:59:18.350 27298-27315/bletest D/BluetoothGatt: onClientRegistered() - status=0 clientIf=6
08-20 14:59:18.351 27298-27888/bletest D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(185299140)

#### Expected result

Log.d(TAG, "Connection established"); should be called. It is called when autoConnect=false or the library source code is modified so only connectGattCompat(...) is used.

Logs:

08-20 14:57:46.612 23982-23982/bletest D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(805970639)
08-20 14:57:46.613 23982-24778/bletest D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(805970639)
08-20 14:57:46.624 23982-23982/bletest V/RxBle#BleConnectionCompat: Connecting without reflection
08-20 14:57:46.625 23982-23982/bletest D/BluetoothGatt: connect() - device: 6F:14:AB:E7:6D:01, auto: true
08-20 14:57:46.625 23982-23982/bletest D/BluetoothGatt: registerApp()
08-20 14:57:46.625 23982-23982/bletest D/BluetoothGatt: registerApp() - UUID=e14d9e6e-dd10-4487-be70-5a51f0629b4d
08-20 14:57:46.628 23982-24020/bletest D/BluetoothGatt: onClientRegistered() - status=0 clientIf=5
08-20 14:57:46.630 23982-24778/bletest D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(805970639)
08-20 14:57:46.846 23982-24020/bletest D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=6F:14:AB:E7:6D:01
08-20 14:57:46.846 23982-24020/bletest D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
08-20 14:57:46.850 23982-24789/bletest D/MainActivity: Connection established

Moreover

Could you please elaborate more on the used reflection workaround? Could you provide a list of affected firmwares / Android versions. Maybe the workaround should be disabled by default and used on some devices instead of enabling it by default for all the devices?

Anyways, thanks for your hard work, the library is great and super intuitive to use :)

IncompatibleClassChangeError Exception when creating client

Summary

IncompatibleClassChangeError Exception when creating client:

rxBleClient = RxBleClient.create(getApplicationContext());

Preconditions

N/A

Steps to reproduce actual result

  1. Create an activity
  2. Try Creating a client

    Actual result

java.lang.IncompatibleClassChangeError: The method 'void com.polidea.rxandroidble.internal.radio.RxBleRadioImpl.com_polidea_rxandroidble_internal_radio_RxBleRadioImpl_lambda$new$73()' was expected to be of type direct but instead was found to be of type virtual (declaration of 'java.lang.reflect.ArtMethod' appears in /system/framework/core-libart.jar)
                                                       at com.polidea.rxandroidble.internal.radio.RxBleRadioImpl.access$lambda$0(Unknown)
                                                       at com.polidea.rxandroidble.internal.radio.RxBleRadioImpl$$Lambda$1.run(Unknown)
                                                       at java.lang.Thread.run(Thread.java:818)

Expected result

Getting a client


I've copied the code from the first example, pretty much 1:1, exception creation of the client. If I do it in the onCreate method of the activity, I get the Exception.

Is it not possible to get the client directly from inside the Activity?

Library should provide helper for binding devices

Summary

When pairing device (initiating connection for the first time) bluetooth device requests for PIN which is displayed as notification. Another approach is to display this request as popup dialog. Does the library support displaying popup dialog for PIN input? I can try and submit PR if not.

Kind Regards

Periodicity reading rssi

Please explain the correct way of reading rssi with a certain periodicity using reactive programming. But I do not have sufficient experience in the rx. Any help would be useful to me.

What's the meanings of autoconnect?

It's the normal meaning.
Or it's same as autoConnect in BluetoothDevice

[ * @param autoConnect Whether to directly connect to the remote device (false)
 *                    or to automatically connect as soon as the remote
 *                    device becomes available (true).](url)

How to detect removed devices while scanning

Is there a way to see, if a device is out of range while scanning?

I always get devices and they are always visible. But how do I handle devices, which are going offline while scanning? I need to cleanup my list.

Performance

Has anyone noticed performance degradation by using the library? Or is it a common issue? I've been achieving, on average, 1.5kbits/s when exchanging data with another Android device.

On some device, gatt.close() may cause problem: can't find specified device later when scanning

Summary


On some devices, such as Huawei hornor. After user directly switched to glucose device power off which was connected and written data before, The device (Huawei) can't find previous glucose device again when scanning. But I found that ,if I remove the gatt.close() method after disconnected, the Huawei device can find glucose. So Can you help me how to process this special situation in an elegant way when using your library. Temporarily , I just remove gatt.close() in RxBleRadioOperationDisconnect class. But I know, it's not the best choice.

Preconditions


Successfully connected to glucose device and written data

Steps to reproduce actual result

  1. Directly power off glucose device
  2. Power on glucose device again.

Actual result


Can't find previous glucose device when scanning, except restart the whole BT(turn off BT and then turn on in Android device settings).

Expected result


Normal scan and find previous glucose device.

@uKL @DariuszAniszewski

Encountering MissingBackPressureException with lots of characteristic changes

First of all thank you for this library. You guys have done a seriously awesome job.

Occasionally I am encountering a MissingBackPressureException when I create an observable that establishes a connection and listens for characteristic changes (setupNotification(CHARACTERISITC))

In my use case there are lots of characteristic changes that we use to read data from the device but occasionally when there are more than normal it will crash with a MissingBackPressureException.

Have you guys encountered this / known behavior? Is there a specific way you recommend dealing with this on the app side to avoid changes to the lib? Thanks again.

rx.exceptions.CompositeException: 4 exceptions occurred. at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:255) at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:797) at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:690) at rx.internal.operators.OperatorMerge$MergeSubscriber.emitScalar(OperatorMerge.java:416) at rx.internal.operators.OperatorMerge$MergeSubscriber.tryEmit(OperatorMerge.java:340) at rx.internal.operators.OperatorMerge$InnerSubscriber.onNext(OperatorMerge.java:825) at rx.subjects.SubjectSubscriptionManager$SubjectObserver.onNext(SubjectSubscriptionManager.java:223) at rx.subjects.PublishSubject.onNext(PublishSubject.java:114) at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1$$Lambda$7.call(Unknown Source) at rx.Observable$27.onNext(Observable.java:8571) at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:215) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818)

Is possible to subscribe to multiple characteristic notifications at once? Also to read? And to combine more read/notify operations?

Is there any possibility where I can subscribe to those characteristics that allow notifications in one row?

In the demos you only listen to one characteristic at a time but would be great to have in RxBleDeviceServices:

public Observable<List<BluetoothGattCharacteristic>> getCharacteristics(@NonNull UUID serviceUuid) {
    return getService(serviceUuid).map(BluetoothGattService::getCharacteristics)
        .filter(bluetoothGattCharacteristics -> (bluetoothGattCharacteristics != null && bluetoothGattCharacteristics.size() > 0))
        .switchIfEmpty(Observable.error(new BleCharacteristicNotFoundException(serviceUuid)));
  }

As I'm quite new to RxJava (and your codebase) I don't know if is a good idea to do this (or even the approach) but I would gladly code it if I'm given more explanations on how to achieve it (or maybe I'm thinking a bad assumption and is already implemented).

The use case is that I have one object that acts as a model with different parameters and those parameters are filled with the notifications that each characteristic sends with the updated value. As I have 10 characteristics sending notifications, I have to setup every notification by hand, then listen to the change and then update the object.

Below is an example of what to achieve (don't look too much to the code as I know that is faulty)

connectionObservable.flatMap(RxBleConnection::discoverServices)
        .flatMap(rxBleDeviceServices -> rxBleDeviceServices.getCharacteristics(ALDeviceUuids.SERVICE_ID))
        .flatMapIterable(bluetoothGattCharacteristics -> bluetoothGattCharacteristics)
        .filter(characteristic -> BleUtils.hasNotificationProperty(characteristic.getProperties()))
        .doOnNext(rxBleConnection::setupNotification)
        .observeOn(AndroidSchedulers.mainThread())
        //.flatMap(characteristic -> characteristic)
        .subscribe(this::onNotificationWritten);

The same could apply if I would like to read all the values at once by filtering those characteristics that allow reading.

Thanks :)

Suggested way of scanning when connecting to multiple devices

Some questions


- What the best practice to unsubscribe scanning if I want to connect to many devices and communicate ? in Activity#onDestory? or like the following code
Subscription subscribe = rxBleClient.scanBleDevices().subscribe(new Action1<RxBleScanResult>() {
    @Override
    public void call(RxBleScanResult rxBleScanResult) {
        // connection made event source
        Observable<RxBleConnection> connectionSrc = rxBleScanResult
                .getBleDevice()
                .establishConnection(activity, false);

        // when found the device, stop scanning
        subscribe.unsubscribe();
   }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.