Android BLE Library

repository·main·Indexed 25 days ago

https://github.com/nordicsemi/android-ble-library

An Android library by Nordic Semiconductor designed to simplify Bluetooth Low Energy (BLE) development. It provides connection management, service discovery, data packet splitting/merging, and GATT server support via the BleManager class. The library includes modules for Kotlin Coroutines and Flow (:ble-ktx), LiveData integration (:ble-livedata), and pre-built Bluetooth SIG profile parsers (:ble-common). Note that it does not provide device scanning functionality.

Tokens
8.4K
Snippets
13
Records
39
Agent score
82%

What's inside android-ble-library

  1. What is the Android BLE Common Library?

    main
    The Android BLE Common Library is an add-on to the main Android BLE Library. It provides pre-built data parsers for various Bluetooth SIG adopted profiles. Instead of manually parsing raw byte arrays from GATT characteristics, you can use these specialized parsers to receive structured, type-safe data. It is compatible with the Android BLE Library starting from version 2.0.
  2. Core features of BleManager

    main

    The BleManager class is the primary entry point for BLE operations and provides:

    • Connection Management: Automatic retries, connection timeouts, and disconnection timeouts.
    • Service & Bonding: Service discovery, optional bonding, and removing bond information.
    • Data Handling: Splitting/merging long packets, Reliable Write support, and MTU/connection priority requests.
    • Advanced BLE: GATT server support (since v2.2), preferred PHY reading/setting (Android Oreo+), and RSSI reading.
    • Lifecycle & Error Handling: Automatic handling of Service Changed indications, operation timeouts, error handling, and logging.
    • Kotlin Support: Coroutines and Flow support (since v2.3).

    Important: This library does not provide scanning functionality. It is recommended to use the Android Scanner Compat Library for device scanning.

  3. Key features of BleManager

    main

    The BleManager class is the primary entry point for BLE operations and provides:

    • Connection Management: Automatic retries and connection timeouts.
    • Service & Discovery: Service discovery and automatic handling of Service Changed indications.
    • Bonding: Optional bonding and removal of bond information.
    • Data Handling: Splitting/merging long packets for reads/writes, and Reliable Write support.
    • Advanced BLE Config: Requesting MTU, connection priority, and preferred PHY.
    • GATT Server: Support for running a local GATT server (since v2.2).
    • Modern Tooling: Kotlin support (coroutines, Flow) and error handling/logging.

    Important Limitation: The library does not provide scanning for Bluetooth LE devices. It is recommended to use the Android Scanner Compat Library for device discovery.

  4. How to implement a BleManager

    main

    To use the library, you must extend BleManager to define your device's high-level API. A BleManager instance is responsible for a single Bluetooth LE peripheral. While you can reuse an instance for a new peripheral after disconnection, it is recommended to create a new instance for each device.

    You must implement the following three methods:

    1. isRequiredServiceSupported(BluetoothGatt gatt): Search gatt.getServices() for required services and characteristics. Return true only if all required services and characteristics are found. Use this method to acquire and store references to your BluetoothGattCharacteristic objects.
    2. onServicesInvalidated(): Called when the device disconnects or services become invalid. Use this to nullify your stored characteristic references.
    3. initialize(): (Optional) Perform device setup, such as enabling notifications or writing initial values. Enqueued operations in this method will complete before onDeviceReady() is called and before the ConnectRequest's done callback triggers.

    Additionally, you should implement your own high-level public methods (e.g., turnLedOn()) that wrap low-level BLE operations like writeCharacteristic.

    class MyBleManager extends BleManager {
        private BluetoothGattCharacteristic fluxCapacitorControlPoint;
    
        @Override
        protected boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) {
            BluetoothGattService fluxCapacitorService = gatt.getService(FLUX_SERVICE_UUID);
            if (fluxCapacitorService != null) {
                fluxCapacitorControlPoint = fluxCapacitorService.getCharacteristic(FLUX_CHAR_UUID);
            }
            return fluxCapacitorControlPoint != null;
        }
    
        @Override
        protected void initialize() {
            requestMtu(517).enqueue();
        }
    
        @Override
        protected void onServicesInvalidated() {
            fluxCapacitorControlPoint = null;
        }
    
        public void enableFluxCapacitor() {
            writeCharacteristic(fluxCapacitorControlPoint, Flux.enable(), BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
                .enqueue();
        }
    }
  5. Implement Client-Server BLE interaction

    main

    The Trivia example demonstrates a client-server architecture using the Android BLE Library.

    • Server Role: Use a BleServerManager to open the GATT server and initialize services. The server advertises services using a randomly generated service UUID to make them discoverable by remote devices.
    • Client Role: The client scans for advertising devices matching the specific service UUID and connects to the first one discovered. A BleManager instance is used to manage the connection and handle communication with the peripheral.

    Data is exchanged between the client and server using Protocol Buffers (Protobuf) for efficient transmission.

  6. Use GATT Server in the BLE Library

    main

    Since version 2.2, the library supports implementing a local GATT server on the Android device. This allows the device to act as a peripheral.

    Key server-side operations include:

    • wait for read
    • wait for write
    • send notification
    • send indication
    • set characteristic value
    • set descriptor value

    To use a device as a server-only implementation, call attachClientConnection(BluetoothDevice) instead of connect(BluetoothDevice).

  7. Use Kotlin Coroutines and Flow with BLE

    main

    The :ble-ktx module provides extensions for modern asynchronous programming in Kotlin.

    Using Coroutines: When using coroutines, use the .suspend() method on a Request instead of enqueue() or await().

    Using Flows for Notifications: You can register for notifications or indications and convert them into a Flow. You can also use JsonMerger to handle data sent in multiple packets.

    setNotificationCallback(characteristic)
       .merge(JsonMerger()) // Optional: merges multi-packet JSON
       .asFlow()

    Other Flow features:

    • .stateAsFlow() and .bondingStateAsFlow() in BleManager return flows for connection and bond states.
    • Progress for split/merged data can be observed via splitWithProgressFlow(...) and mergeWithProgressFlow(...).
    • ValueChangedCallback can be converted to a flow using .asResponseFlow() or .asValidResponseFlow() (v2.4+).
    setNotificationCallback(characteristic)
       .merge(JsonMerger()) // Example of how to use JsonMerger, optional
       .asFlow()
  8. Migrate from version 1.x to 2.x

    main

    When upgrading to version 2.0 or higher, several core architectural changes require updates to your BleManager implementation:

    1. Initialization: Replace initGatt(BluetoothGatt) with initialize(). Instead of returning a Deque<Request>, use initialize() to set up callbacks and call .enqueue() on your initialization requests.
    2. Request-based API: BLE operation methods (e.g., writeCharacteristic(...)) now return a Request object instead of a boolean. Use these Request objects and their callbacks instead of the deprecated BleManagerGattCallback GATT callbacks.
    3. Connection Configuration: Replace shouldAutoConnect() with .useAutoConnect(boolean) on the ConnectRequest object.
    4. Asynchronous Operations: For connect() and disconnect() methods, you must call .enqueue() when using them asynchronously.
    5. Callback Renaming: onLinklossOccur is renamed to onLinkLossOccurred.
    // Old code (1.x)
    @Override
    protected Deque<Request> initGatt(final BluetoothGatt gatt) {
      final LinkedList<Request> requests = new LinkedList<>();
      requests.add(Request.newEnableNotificationsRequest(characteristic));
      return requests;
    }
    
    // New code (2.x)
    @Override
    protected void initialize() {
      setNotificationCallback(characteristic)
        .with(new DataReceivedCallback() {
          @Override
          public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
            ...
          }
        });
      enableNotifications(characteristic)
        .enqueue();
    }
  9. Migrate from version 2.1 to 2.2

    main

    In version 2.2, BleManager is no longer a generic class. This change affects how you handle connection and bonding states.

    Quick Migration (using deprecated API)

    To transition quickly, change your base class to LegacyBleManager. You must ensure getGattCallback() returns a new object rather than a class property to avoid null pointer issues during construction:

    class MyBleManager extends LegacyBleManager<MyBleManagerCallbacks> {
        @NonNull
        @Override
        protected BleManagerGattCallback getGattCallback() {
            return new MyBleManagerGattCallback();
        }
    }

    Proper Migration

    1. Remove Generics: Remove the type parameter from your BleManager implementation class.
    2. Replace Callbacks:
      • Use setConnectionObserver(observer) for connection state updates (replaces setGattCallbacks).
      • Use setBondingObserver(observer) for bonding events.
      • Manage application-level callbacks manually within your manager.
    3. Connection Observer Mapping:
      • onServicesDiscovered is removed.
      • onLinkLossOccurred $\rightarrow$ onDeviceDisconnected with ConnectionObserver#REASON_LINK_LOSS.
      • onDeviceNotSupported $\rightarrow$ onDeviceDisconnected with ConnectionObserver#REASON_NOT_SUPPORTED.
    4. LiveData Integration: For easier state management, extend ObservableBleManager and use no.nordicsemi.android:ble-livedata to access getState() and getBondingState() as LiveData objects.
    class MyBleManager extends LegacyBleManager<MyBleManagerCallbacks> {
    
        // [...]
    
        @NonNull
        @Override
        protected BleManagerGattCallback getGattCallback() {
            // Before 2.2 it was allowed to return a class property here, but properties are initiated
            // after the constructor, so they would still be null here. Instead, create a new object:
            return new MyBleManagerGattCallback();
        }
    
        // [...]
    
    }
  10. Install the Android BLE Library via Maven Central

    main

    To use the Android BLE Library, add the following dependency to your build.gradle file. The library is available on Maven Central.

    Core Library:

    implementation 'no.nordicsemi.android:ble:2.11.0'

    Kotlin Extensions (Coroutines & Flow): If you are using Kotlin and want support for coroutines and Flow, add the :ble-ktx module:

    implementation 'no.nordicsemi.android:ble-ktx:2.11.0'

    Common Bluetooth SIG Parsers: To include a set of parsers for common Bluetooth SIG characteristics, add:

    implementation 'no.nordicsemi.android:ble-common:2.11.0'

    LiveData Integration: To use ObservableBleManager which provides state and bondingState properties as androidx.lifecycle.LiveData, add:

    implementation 'no.nordicsemi.android:ble-livedata:2.11.0'

    Note on Versions:

    • The last version not migrated to AndroidX is 2.0.5.
    • Version 1.x is no longer supported; migrate to 2.x.
    implementation 'no.nordicsemi.android:ble:2.11.0'
  11. Configure required permissions for BLE and Internet

    main

    To run applications like the Trivia example, you must handle the following permissions:

    Bluetooth and Location

    • Android 6 to 11: You must request Location Permission and ensure Location services are enabled to obtain BLE scan and advertising results.
    • Android 12 and newer: New Bluetooth permissions are used. You can request BLUETOOTH_SCAN with the usesPermissionFlags="neverForLocation" parameter, which allows you to perform scans without requiring location permissions (provided you do not use scan results for location-related data).

    Internet

    • Internet Access: Required if the application needs to fetch data from external web APIs (e.g., the Trivia API).