1

I badly need this to proceed further in my application.

I'm very much familiar with Android BLE and using for years.

I have the below code to enable notification and it is working for years with my peripheral. onCharacteristicChanged() method is called with "OK_N1" when notification is enabled.

private void enableNotification(String serviceUUID, String characteristicUUID) {
    if (bluetoothGatt == null) {
        return;
    }
    BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(serviceUUID));
    if (service == null) {
        return;
    }
    BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicUUID));

    bluetoothGatt.setCharacteristicNotification(characteristic, true);
    enableDescriptor(characteristic);
}

private void enableDescriptor(BluetoothGattCharacteristic bluetoothGattCharacteristic) {
    if (bluetoothGatt == null) {
        return;
    }
    BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(
            UUID.fromString(PodsServiceCharacteristics.CLIENT_CHARACTERISTIC_CONFIG));
    if (descriptor == null)
        return;
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    bluetoothGatt.writeDescriptor(descriptor);
}

Now, I am using Polidea RxAndroidble(ver 1.7.0) with RxJava2 to make things easy.

I have the following code with Polidea's RxAndroidBle which is not working.

public void enableNotifications(@NotNull String[] characteristics) {
    if (isConnected()) {
        mNotificationSubscriber = mRxBleConnection.setupNotification(UUID.fromString(characteristics[0]))
                .doOnNext(notificationObservable -> notificationHasBeenSetUp())
                .flatMap(notificationObservable -> notificationObservable)
                .subscribe(this::onNotificationReceived, this::onNotificationSetupFailure);
    }
}

private void onNotificationReceived(byte[] bytes) {
    Log.i(TAG, "onNotificationReceived");
}

private void onNotificationSetupFailure(Throwable throwable) {
    Log.i(TAG, "onNotificationSetupFailure" + throwable.getMessage());
}

private void notificationHasBeenSetUp() {
    Log.i(TAG, "notificationHasBeenSetUp");
}

notificationHasBeenSetUp() is called but onNotificationReceived() is not called, where I get "OK_N1" bytes

Sundeep1501
  • 1,510
  • 1
  • 18
  • 28

1 Answers1

2

It is probably because your peripheral sends a notification right after the Client Characteristic Configuration Descriptor is set.

By default RxAndroidBle sets up notifications fully before emitting the Observable<byte[]> i.e. the CCC descriptor has to be successfully written before the emission.

Since library version 1.8.0 it is possible to use NotificationSetupMode.QUICK_SETUP which emits Observable<byte[]> as soon as possible — before the CCC descriptor is written allowing to capture such early notifications/indications.

mRxBleConnection.setupNotification(bluetoothGattCharacteristic, NotificationSetupMode.QUICK_SETUP)

Pre 1.8.0 version answer

To not miss any "early" emissions one may leverage NotificationSetupMode.COMPAT in which the library does not handle writing the CCC descriptor*.

(...)

mNotificationSubscriber = mRxBleConnection.discoverServices()
        .flatMap(rxBleDeviceServices -> rxBleDeviceServices.getCharacteristic(characteristicUuid))
        .flatMapObservable(bluetoothGattCharacteristic -> {
            BluetoothGattDescriptor cccDescriptor = bluetoothGattCharacteristic.getDescriptor(PodsServiceCharacteristics.CLIENT_CHARACTERISTIC_CONFIG);
            Completable enableNotificationCompletable = mRxBleConnection.writeDescriptor(cccDescriptor, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            Completable disableNotificationCompletable = mRxBleConnection.writeDescriptor(cccDescriptor, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE).onErrorComplete();
            return mRxBleConnection.setupNotification(bluetoothGattCharacteristic, NotificationSetupMode.COMPAT)
                    .doOnNext(notificationObservable -> notificationHasBeenSetUp())
                    .flatMap(notificationObservable -> notificationObservable)
                    .mergeWith(enableNotificationCompletable)
                    .doOnDispose(disableNotificationCompletable::subscribe) // fire and forget
                    .share(); // this can be omitted but I put it here in case the resulting `Observable<byte[]>` would be shared among multiple subscribers
        })
        .subscribe(this::onNotificationReceived, this::onNotificationSetupFailure);

(...)

I think since this is apparently quite common situation it will be handled by the library at some point.

* NotificationSetupMode.COMPAT was introduced mainly for BLE peripherals that do not follow BLE specification and do not have CCC descriptor yet will send notifications all the time

Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21