A reactive, interface-driven central role Bluetooth LE library for Android

Overview

RxCentralBle Build Status

RxCentralBle provides a simple reactive paradigm for connecting to and communicating with Bluetooth LE peripherals from the central role. It is ideal for all applications - in particular those that require multiple concurrent Bluetooth LE integrations.

RxCentralBle avoids many known Android pitfalls, including limitations in Android 7 & 8 around scanning operations, serial FIFO queueing for GATT, and others.

RxCentralBle powers Uber's Bluetooth LE integrations on Android, including the next-generation Uber Beacon.

NEW! Uber Engineering Blog article detailing RxCentral cross-platform design and techniques for Android.

Check out the equivalent iOS library: RxCBCentral.

Key Features

  • Reactive; subscribe to actions to trigger them, dispose to stop
  • Built-in operation queue; respects the serial nature of Android's BluetoothGatt
  • Built-in GATT write segmentation; all writes are automatically chunked into MTU-sized segments
  • Interface-driven; customize the library with your own implementations
  • Manager-based; two managers for all connectivity and communication

Applicability

RxCentralBle optimizes for the following use cases:

  • Where the ability to connect to and communicate with a Bluetooth 4.0+ LE peripheral is needed
  • Where the peripheral is Bluetooth 4.0+ LE compliant and acts per the specification
  • Where the peripheral does not require Bluetooth 4.0 specified authentication

Download

Available on Maven Central:

dependencies {
  implementation 'com.uber.rxcentralble:rx-central-ble:1.2.0'
}

Usage

The below demonstrates simple usage of RxCentralBle. Check out the Wiki for details!

Bluetooth Detection

Use the BluetoothDetector to detect the state of Bluetooth:

BluetoothDetector bluetoothDetector;
Disposable detection;

// Use the detector to detect Bluetooth state.
detection = bluetoothDetector
   .enabled()
   .subscribe(
       enabled -> {
         // Tell the user to turn on Bluetooth if not enabled
       }
   );
   

Dispose of your subscription to stop detection.

// Stop Bluetooth detection.
detection.dispose();

Connection Management

Use the ConnectionManager to manage the lifecycle of connections to a peripheral and supply a fresh Peripheral to the PeripheralManager on every connection.

ScanMatcher scanMatcher;
ConnectionManager connectionManager;
PeripheralManager peripheralManager;
Disposable connection

// Connect to a peripheral.  
connection = connectionManager
    .connect(scanMatcher, DEFAULT_SCAN_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT)
    .subscribe(
        peripheral -> {
          // Inject the latest connected Peripheral into your PeripheralManager.
          peripheralManager.setPeripheral(peripheral);
        },
        error -> {
          // Connection lost.
        };

Dispose of your subscription to disconnect.

// Disconnect.
connection.dispose();

Because this is a reactive library, you can leverage Rx retries to attempt to reconnect to a peripheral if a scan fails.

ScanMatcher scanMatcher;
ConnectionManager connectionManager;
PeripheralManager peripheralManager;
Disposable connection

// Support retries for connection. 
connection = connectionManager
    .connect(scanMatcher, DEFAULT_SCAN_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT)
    .retryWhen(
        errorObservable -> 
            // Custom retry logic
    )
    .subscribe(
        peripheral -> {
          // Inject the latest connected peripheral into your PeripheralManager.
          peripheralManager.setPeripheral(peripheral);
        };

Peripheral Management

After injecting the connected Peripheral into your PeripheralManager, you can then queue operations and the PeripheralManager will ensure these are executed in a serial FIFO fashion. The PeripheralManager is thread safe, so multiple consuming threads can queue operations and they will be reliably executed in the order they are subscribed.

PeripheralManager peripheralManager;
Write write;
Disposable queued;

// Queue a write operaiton.
queued = peripheralManager
  .queueOperation(write))
  .subscribe(
       irrelevant -> {
         // Write was successful.
       },
       error -> {
         // Write failed.
       });

Dispose of your subscription to dequeue (i.e. cancel).

// Cancel the write operation if it hasn't begun execution.
queued.dispose();

Sample App

The included sample application allows you to connect to any Bluetooth LE peripheral by name and query the Generic Access, Device Information, and Battery services. Feel free to improve upon the sample and submit PRs to help the RxCentralBle community.

License

Copyright (C) 2018 Uber Technologies

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.
Comments
  • How does it compare to RxAndroidBle?

    How does it compare to RxAndroidBle?

    Thank you for open sourcing this library, it looks very nice and polished. There is a popular reactive Android bluetooth library https://github.com/Polidea/RxAndroidBle I'm wodering how does RxCentralBle compare to RxAndroidBle and why/when should I pick which. Maybe you could add this comparison to readme/wiki? Thanks!

    I know you're planning to release iOS version of RxCentralBle and there is a popular iOS reactive library called RxBluetoothKit so it would be great to have a comparison of those two as well :)

    opened by mzgreen 4
  • Bump to AndroidX

    Bump to AndroidX

    Thank you for contributing to RxCentralBle. Before pressing the "Create Pull Request" button, please provide the following:

    • In order to support AndroidX, Jetifier needs to be run on the project and all the support libraries need to be migrated. This diff is the result of that effort.
    opened by EdbertChan 1
  • updating readme with retry information and fixing spelling

    updating readme with retry information and fixing spelling

    Fixing the incorrect spelling of "Peripheral" and adding an example in the README for leveraging Rx retry operations when attempting to perform a connection.

    opened by jpsoultanis 1
  • Support for ScanFilter

    Support for ScanFilter

    There may be cases where consumers want to attempt using Android's ScanFilter to save battery, despite known issues with ScanFilter'ing.

    Provide a degree of support for this.

    opened by kbabcockuf 1
  • Improved ScanMatcher to perform observable transformation

    Improved ScanMatcher to perform observable transformation

    The ScanMatcher interface was limited to performing synchronous comparisons. Using an ObservableTransformer to perform matching provides greater flexibility and allows for performing asynchronous comparisons.

    opened by kbabcockuf 1
  • Android 12 permissions

    Android 12 permissions

    Updating the Sample App to compile against and target API 31 to test RxCentralBle against new Android 12 permissions.

    Also removed permissions from the RxCentralBle manifest to ensure consumers of the library have full flexibility of how they want to define these.

    Finally, removing the deprecated LollipopScanner. Folks need to use the throttled scanner :)

    opened by kbabcockuf 0
  • Android X Bump

    Android X Bump

    In order to support AndroidX, Jetifier needs to be run on the project and all the support libraries need to be migrated. This diff is the result of that effort.

    It looks like the change was reverted due to a pipeline error but all tests pass on my side.

    https://imgur.com/a/18al0cw

    opened by EdbertChan 0
  • Update README.md with iOS RxCBCentral repo link

    Update README.md with iOS RxCBCentral repo link

    Updating README to link to iOS RxCBCentral repo

    Thank you for contributing to RxCentralBle. Before pressing the "Create Pull Request" button, please provide the following:

    • [x] A description about what and why you are contributing, even if it's trivial.

    • [x] The issue number(s) or PR number(s) in the description if you are contributing in response to those.

    • [x] If applicable, unit tests.

    opened by jpsoultanis 0
  • Thread safe scanners

    Thread safe scanners

    Scan operations via Scanner implementations (except the deprecated LollipopScanner) are now thread safe.

    The CoreConnectionManager is still not thread safe.

    opened by kbabcockuf 0
  • Deprecated Gatt in favor of Peripheral for interface naming conventions

    Deprecated Gatt in favor of Peripheral for interface naming conventions

    Usage of the term Peripheral is more descriptive and easier to grok as a newcomer to Bluetooth LE. This change should lend much greater clarity to the library design and purpose of interfaces.

    No functional changes.

    opened by kbabcockuf 0
  • Rename Gatt Interfaces

    Rename Gatt Interfaces

    Although usage of "Gatt" in interface naming is accurate, we have discovered usage of the term "Peripheral" lends greater clarity to the library.

    Rename interfaces with using Peripheral instead of Gatt naming conventions.

    opened by kbabcockuf 0
  • Use version compiled with JitPack

    Use version compiled with JitPack

    Hi 👋

    I've just opened a PR https://github.com/uber/RxCentralBle/pull/49 to fix a bug and I wanted to use that version with JitPack.

    The library is compiled successfully with JitPack, but when I use it in my project it does not find some dependencies like RxRelay

    Caused by: java.lang.ClassNotFoundException: com.jakewharton.rxrelay2.Relay

    There is an StackOverflow related issue https://stackoverflow.com/questions/68415734/packagemanagernamenotfoundexception-for-rxandroidble

    Adding the libraries again in my project solves the problem, but I'd like to know if there is anything that can be done to the library so it became compatible with JitPack.

    Here you can find the JitPack log when compiling the library. There are some JavaDoc errors, but I don't know if they are related.

    Thanks!

    opened by eduardbosch 1
  • fix: Close gatt when disconnecting

    fix: Close gatt when disconnecting

    Hi 🎉

    First of all, thanks for this library 🙂

    I've found a bug when connecting to a specific BluetoothDevice when we retry the connections too many times. I've fixed the bug in this PR and updated the example to check and illustrate the bug.

    Description

    When connecting to a device through MAC, using a BluetoothDevice, retrying the connection crashes due to too many active GATT clients. By closing the GATT client after each direct connection to a BluetoothDevice, we release the unused connections and clients and we can keep trying to connect to a BluetoothDevice without reaching the maximum connections limit.

    I've seen in this video https://www.youtube.com/watch?v=jDykHjn-4Ng&t=2785s that we must close the GATT connection when the connection has not been established and that we cannot rely on the onConnectionStateChange callback to be called always. The article recommends closing the GATT with a timeout and canceling the timeout if the callback to close the GATT is called.

    Reproduce the bug

    1. Modify the connection timeout when connecting to a BluetoothDevice to 1_000.
    2. Open the example
    3. Select connect by MAC
    4. Introduce the MAC of any Bluetooth Device
    5. Select connect

    After some retries, the app crashes with an error like this due to too many connections (see clientIf). On my phone, it crashes after 32 connections. It may differ on other devices.

    Show log and stacktrace
    I/MainActivity: Connect to:  90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=9d756657-aee1-4e5f-98d7-c9b8684acc35
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=5
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,2,2,6 interval=1122
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,1,3,0 interval=1692
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=ba468f23-3839-4a5e-b3a5-519fd3606ee7
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=6
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,1,4,0 interval=2195
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,1,5,0 interval=2698
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=e5f9901a-9cd6-4c8a-87cf-c990648be642
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=7
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,1,6,0 interval=3201
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,1,7,0 interval=3704
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=5900082f-0ece-4e24-aa97-82324209f5fc
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=8
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,1,8,0 interval=4206
    D/HwAppInnerBoostImpl: asyncReportData com.uber.rxcentralble.sample,1,1,9,0 interval=4709
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=7976fc99-c31f-4db3-b2d5-9420e92159af
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=9
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=8c3c4dc7-006b-46eb-9215-04c6e32cb66f
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=10
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=f454a3b8-c415-4509-9da1-cd4478874690
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=11
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=70f253ab-9af2-4657-b86d-539f50718628
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=12
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=2fdae395-7391-4cec-9533-1ae262a57e95
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=13
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=2ab8576f-2653-478a-bf3c-c89c14306a24
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=14
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=dbe4b428-0804-4ab7-9524-7643276f9b4d
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=15
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=29b10e39-cea4-476d-98d0-b1d18557a008
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=16
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=81f37246-8160-4bd4-9b32-2e6ea7769a7a
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=17
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=985570af-0d95-4273-8340-2b0f5f8d1925
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=18
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=60bd1ef2-534b-44f1-84d3-29b7db1bfaa7
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=19
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=2afd49f8-140a-4c6a-bd80-a9beeaf78c32
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=20
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=9108e685-5b67-4a35-ac97-8806aaa03a27
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=21
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=04c26d10-0e3b-4d5e-9e83-bf5814ca51ec
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=22
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=dece58d5-9ab0-416d-a183-dfae361d0508
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=23
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=fe287d33-2591-4ac5-bdc5-b0d0893205b1
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=24
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=d8174ff1-78a4-4141-af2d-c58d2ca4f348
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=25
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=835d7be4-240e-4961-8e25-cdd452f83dca
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=26
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=e28364e4-19e9-48f4-90ad-7c46d15b29af
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=27
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=e13bfa7d-2466-41e6-8062-dc28e709d186
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=28
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=db1e6093-6936-4547-bdf4-9102fadb577e
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=29
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=c71d7bf2-a50d-4381-8458-fcaf50c2e6ef
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=30
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=3052ef80-41bf-46c0-9d7c-8dccfb14982a
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=31
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=2ac65745-c60b-4c76-b165-4ae5959de6ee
    D/BluetoothGatt: onClientRegistered() - status=0 clientIf=32
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=55b05a46-ad15-41d6-bd20-2f795b2b7893
    D/BluetoothGatt: onClientRegistered() - status=133 clientIf=0
    D/MainActivity: onConnectionStateChange - Status: 257 | State: 0
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=afdc1315-7e6c-4e4c-921c-7145e341f95d
    D/BluetoothGatt: onClientRegistered() - status=133 clientIf=0
    D/MainActivity: onConnectionStateChange - Status: 257 | State: 0
    D/BluetoothGatt: cancelOpen() - device: 90:35:EA:66:41:C4
    D/BluetoothGatt: close()
    D/BluetoothGatt: unregisterApp() - mClientIf=0
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=724f2cfb-c5a3-4e2a-9823-d120ea4a10bd
    D/BluetoothGatt: onClientRegistered() - status=133 clientIf=0
    D/MainActivity: onConnectionStateChange - Status: 257 | State: 0
    W/System.err: io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | DISCONNECTION
    W/System.err:     at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableCombineLatest$LatestCoordinator.innerError(ObservableCombineLatest.java:262)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableCombineLatest$CombinerObserver.onError(ObservableCombineLatest.java:311)
    W/System.err:     at io.reactivex.observers.SerializedObserver.onError(SerializedObserver.java:153)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableWithLatestFrom$WithLatestFromObserver.onError(ObservableWithLatestFrom.java:90)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableRefCount$RefCountObserver.onError(ObservableRefCount.java:203)
    W/System.err:     at io.reactivex.internal.util.NotificationLite.accept(NotificationLite.java:243)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableReplay$BoundedReplayBuffer.replay(ObservableReplay.java:695)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableReplay$ReplayObserver.replayFinal(ObservableReplay.java:412)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableReplay$ReplayObserver.onError(ObservableReplay.java:377)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableDoFinally$DoFinallyObserver.onError(ObservableDoFinally.java:85)
    W/System.err:     at io.reactivex.internal.observers.DisposableLambdaObserver.onError(DisposableLambdaObserver.java:64)
    W/System.err:     at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onError(ObservableDoOnEach.java:117)
    W/System.err:     at io.reactivex.internal.util.NotificationLite.accept(NotificationLite.java:243)
    W/System.err:     at io.reactivex.subjects.BehaviorSubject$BehaviorDisposable.test(BehaviorSubject.java:569)
    W/System.err:     at io.reactivex.subjects.BehaviorSubject$BehaviorDisposable.emitNext(BehaviorSubject.java:564)
    W/System.err:     at io.reactivex.subjects.BehaviorSubject.onError(BehaviorSubject.java:281)
    W/System.err:     at com.uber.rxcentralble.core.CorePeripheral$1.onConnectionStateChange(CorePeripheral.java:540)
    W/System.err:     at android.bluetooth.BluetoothGatt$1$1.run(BluetoothGatt.java:169)
    W/System.err:     at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:774)
    W/System.err:     at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:39)
    W/System.err:     at android.bluetooth.BluetoothGatt$1.onClientRegistered(BluetoothGatt.java:164)
    W/System.err:     at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:57)
    W/System.err:     at android.os.Binder.execTransact(Binder.java:739)
    W/System.err: Caused by: com.uber.rxcentralble.ConnectionError: DISCONNECTION
    W/System.err: 	... 7 more
    W/System.err: Caused by: com.uber.rxcentralble.PeripheralError: CONNECTION_FAILED
    W/System.err: 	... 7 more
    D/BluetoothGatt: connect() - device: 90:35:EA:66:41:C4, auto: false
    D/BluetoothGatt: registerApp()
    D/BluetoothGatt: registerApp() - UUID=ee2fcf0e-5683-4306-9422-31add30590b8
    E/AndroidRuntime: FATAL EXCEPTION: Binder:30553_5
        Process: com.uber.rxcentralble.sample, PID: 30553
        io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | DISCONNECTION
            at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
            at io.reactivex.internal.operators.observable.ObservableCombineLatest$LatestCoordinator.innerError(ObservableCombineLatest.java:262)
            at io.reactivex.internal.operators.observable.ObservableCombineLatest$CombinerObserver.onError(ObservableCombineLatest.java:311)
            at io.reactivex.observers.SerializedObserver.onError(SerializedObserver.java:153)
            at io.reactivex.internal.operators.observable.ObservableWithLatestFrom$WithLatestFromObserver.onError(ObservableWithLatestFrom.java:90)
            at io.reactivex.internal.operators.observable.ObservableRefCount$RefCountObserver.onError(ObservableRefCount.java:203)
            at io.reactivex.internal.util.NotificationLite.accept(NotificationLite.java:243)
            at io.reactivex.internal.operators.observable.ObservableReplay$BoundedReplayBuffer.replay(ObservableReplay.java:695)
            at io.reactivex.internal.operators.observable.ObservableReplay$ReplayObserver.replayFinal(ObservableReplay.java:412)
            at io.reactivex.internal.operators.observable.ObservableReplay$ReplayObserver.onError(ObservableReplay.java:377)
            at io.reactivex.internal.operators.observable.ObservableDoFinally$DoFinallyObserver.onError(ObservableDoFinally.java:85)
            at io.reactivex.internal.observers.DisposableLambdaObserver.onError(DisposableLambdaObserver.java:64)
            at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onError(ObservableDoOnEach.java:117)
            at io.reactivex.internal.util.NotificationLite.accept(NotificationLite.java:243)
            at io.reactivex.subjects.BehaviorSubject$BehaviorDisposable.test(BehaviorSubject.java:569)
            at io.reactivex.subjects.BehaviorSubject$BehaviorDisposable.emitNext(BehaviorSubject.java:564)
            at io.reactivex.subjects.BehaviorSubject.onError(BehaviorSubject.java:281)
            at com.uber.rxcentralble.core.CorePeripheral$1.onConnectionStateChange(CorePeripheral.java:540)
            at android.bluetooth.BluetoothGatt$1$1.run(BluetoothGatt.java:169)
            at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:774)
            at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:39)
            at android.bluetooth.BluetoothGatt$1.onClientRegistered(BluetoothGatt.java:164)
            at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:57)
            at android.os.Binder.execTransact(Binder.java:739)
         Caused by: com.uber.rxcentralble.ConnectionError: DISCONNECTION
            at com.uber.rxcentralble.core.CorePeripheral$1.onConnectionStateChange(CorePeripheral.java:540) 
            at android.bluetooth.BluetoothGatt$1$1.run(BluetoothGatt.java:169) 
            at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:774) 
            at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:39) 
            at android.bluetooth.BluetoothGatt$1.onClientRegistered(BluetoothGatt.java:164) 
            at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:57) 
            at android.os.Binder.execTransact(Binder.java:739) 
         Caused by: com.uber.rxcentralble.PeripheralError: CONNECTION_FAILED
            at com.uber.rxcentralble.core.CorePeripheral$1.onConnectionStateChange(CorePeripheral.java:540) 
            at android.bluetooth.BluetoothGatt$1$1.run(BluetoothGatt.java:169) 
            at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:774) 
            at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:39) 
            at android.bluetooth.BluetoothGatt$1.onClientRegistered(BluetoothGatt.java:164) 
            at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:57) 
            at android.os.Binder.execTransact(Binder.java:739)
    

    Other issues that may be related in other libraries

    • https://github.com/android/connectivity-samples/issues/18
    • https://github.com/Reedyuk/blue-falcon/issues/53

    Questions

    ¿Do you think that the GATT connection should be closed too when it's no longer needed instead of just disconnecting?

    opened by eduardbosch 7
  • [Feature Request] Make write type flexible

    [Feature Request] Make write type flexible

    Looking at the core api implementation for write, library itself defaults to WRITE_TYPE_NO_RESPONSE if characteristics supports it. As an api user, this limits the flexibility. Following is the snippet of how core handles the write. There seems to be no way for me to specify WRITE_TYPE_DEFAULT if by default the intended characteristics support WRITE_TYPE_NO_RESPONSE.

    if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)
              == BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) {
            characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
          } else {
            characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
          }
    

    Version number : 1.2.1

    opened by sam33rdhakal 1
  • ConnectionErrors should be represented in ConnectionManagerState

    ConnectionErrors should be represented in ConnectionManagerState

    When observing state of the Connection Manager, you need two sources to fully understand state:

    • Errors that occur from connection subscription
    • State emitted by the Connection Manager

    To make tracking variants of state easier, emit specific errors onto the state stream.

    opened by kbabcockuf 0
Releases(1.2.2)
Owner
Uber Open Source
Open Source Software at Uber
Uber Open Source
[UNMAINTAINED][Android] Bluetooth Serial Port Profile which comfortable to developer application to communication with microcontroller via bluetooth

⚠ WARNING: This project is no longer being maintained Android-BluetoothSPPLibrary Bluetooth Serial Port Profile which comfortable to developer applica

Akexorcist 1.7k Dec 31, 2022
Kotlin Asynchronous Bluetooth Low Energy provides a simple Coroutines-powered API for interacting with Bluetooth Low Energy devices.

Kotlin Asynchronous Bluetooth Low Energy provides a simple Coroutines-powered API for interacting with Bluetooth Low Energy devices.

JUUL Labs 275 Sep 14, 2021
An Android Library for handling Bluetooth Low Energy on Android Easy

An Android Library for handling Bluetooth Low Energy on Android Easy

Leandro SQ 42 Jan 3, 2023
An Android library that solves a lot of Android's Bluetooth Low Energy problems

A library that makes working with Bluetooth LE on Android a pleasure. Seriously.

Nordic Semiconductor 1.4k Jan 7, 2023
A Bluetooth kotlin multiplatform "Cross-Platform" library for iOS and Android

Blue-Falcon A Bluetooth "Cross Platform" Kotlin Multiplatform library for iOS, Android, MacOS, Raspberry Pi and Javascript. Bluetooth in general has t

Andrew Reed 220 Dec 28, 2022
BLESSED Coroutines, a Bluetooth Low Energy (BLE) library for Android using Kotlin Coroutines

BLESSED for Android with Coroutines - BLE made easy BLESSED is a very compact Bluetooth Low Energy (BLE) library for Android 8 and higher, that makes

Martijn van Welie 82 Jan 1, 2023
This library allows for easy access to a Bluetooth LE device's AdRecord and RSSI value. It offers additional functionality for iBeacons.

Bluetooth LE Library for Android This library allows for easy access to a Bluetooth LE device's Advertisement Records. It also offers: A simple runnin

Alexandros Schillings 843 Dec 13, 2022
Smooth communication via bluetooth with other android devices or microcontrollers such as Arduino.

Android Smooth Bluetooth Smooth communication via bluetooth with other android devices or microcontrollers such as Arduino. Getting Started Add Gradle

Mantas Palaima 191 Nov 28, 2022
RxBle: Use Android Bluetooth API in Rx way

RxBle: Use Android Bluetooth API in Rx way A lightweight encapsulation of Android Bluetooth API. Use Android Bluetooth API in Rx way. Support multiple

null 3 Dec 2, 2022
Kotlin Asynchronous Bluetooth Low-Energy

Kable Kotlin Asynchronous Bluetooth Low Energy provides a simple Coroutines-powered API for interacting with Bluetooth Low Energy devices. Usage is de

JUUL Labs 493 Dec 25, 2022
A non-trivial Bluetooth LE app using Kable and app architecture best practices

kable_mvvm_demo The intention of this project is to demonstrate a non-trivial Bluetooth LE app using Kable and app architecture best practices. ⚠️ The

Chris Laplante 14 Aug 18, 2022
BluePass extracts two factor authentication codes (2FA) from SMS and sends them to a paired device via Bluetooth RFCOMM.

BluePass extracts two factor authentication codes (2FA) from SMS and sends them to a paired device via Bluetooth RFCOMM.

Manuel Huber 15 Dec 4, 2022
Open-source weight and body metrics tracker, with support for Bluetooth scales

Open-source weight and body metrics tracker, with support for Bluetooth scales

OliE 1.3k Jan 4, 2023
Simple bluetooth flutter project

bluetooth_simple Simple bluetooth implementation. Getting Started This project is a starting point for a Flutter application. A few resources to get y

Aleksey Vasiliev 0 Nov 25, 2021
The purpose is to share the Internet capability of one device to the entire Bluetooth LAN.

bluenet The purpose is to share the Internet capability of one device to the entire Bluetooth LAN. To make a prototype of a soft bus, or actually, I w

yunlong.wen 1 Jun 28, 2022
User-friendly Lightweight TPM Remote Attestation over Bluetooth

Ultrablue Ultrablue (User-friendly Lightweight TPM Remote Attestation over Bluetooth) is a solution to allow individual users to perform boot state at

ANSSI 32 Jan 2, 2023
A simple, lightweight library intended to take away some of the cruft and tediousness of using the Android BLE.

Blueteeth What Is Blueteeth? Blueteeth is a simple, lightweight library intended to take away some of the cruft and tediousness of using the Android B

Robot Pajamas 103 Nov 26, 2022
Allows Android apps to interact with BLE beacons

Android Beacon Library An Android library providing APIs to interact with beacons. Please visit the project website for how to use this library. IMPOR

AltBeacon 2.7k Dec 28, 2022
🍔 Meals is a small demo app based on modern Android technologies and MVVM architecture

Meals ?? Meals is a small demo app based on modern Android technologies and MVVM architecture. built-in Kotlin, Coroutine, Flow, Retrofit, and Jetpack

Amr Jyniat 4 Nov 6, 2022