24

The BluetoothLeGatt Android BLE example contains the following code:

public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                          boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

    // This is specific to Heart Rate Measurement.
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }
}

My question is basically, why is the marked code specific to Heart Rate Measurement? It seems like having a Client Characteristic Config Descriptor (CCCD) characteristic is the standard way to control characteristic notification, so why doesn't setCharacteristicNotification() take care of writing to it? And since it doesn't do that, what does setCharacteristicNotification() actually do?

I'm pretty new to BLE and there aren't any explanations of it on the internet that don't assume that you already understand it all! So don't assume I know what a CCCD or whatever is! It was difficult enough finding out what CCCD even stands for!

Edit: See also this answer which supports my understanding of CCCDs (and makes me continue to wonder why you have to write to them manually in Android when there is a function that looks like it should do that for you): https://devzone.nordicsemi.com/index.php/what-does-cccd-mean

stkent
  • 19,772
  • 14
  • 85
  • 111
Timmmm
  • 88,195
  • 71
  • 364
  • 509

6 Answers6

15

I think is a litte bit late for give an answer but today I had the same doubt and I found a clear answer. Using setCharacteristicNotification() you enable notification localy (on android device) and setting CCC descriptor to ENABLE_NOTIFICATION_VALUE you enable notification on ble peripheral. In fact for enabling CCC notification you have to use setValue() and writeDescriptor() that are methods used for writing characteristics (in this case characteristics descriptors) to remote device. I found this on: http://processors.wiki.ti.com/index.php/SensorTag_User_Guide

  • 4
    Yes but *why* do you need to enable notification locally? And since enabling notification on the ble peripheral is totally standard, why can't `setCharacteristicNotification()` do that for you too? If you use any BLE Characteristic inspection app (e.g. NRF Control Panel) you can see that they have generic code to enable notification on any characterstic (that allows it). So why does the example code say that it is specific to the heartrate sensor? – Timmmm Oct 10 '14 at 08:51
  • > Yes but why do you need to enable notification locally? And since enabling notification on the ble peripheral is totally standard, why can't setCharacteristicNotification() do that for you too? => Because it is, and remains to this day a poorly implemented API – axa Feb 17 '23 at 05:46
13

Here is an excerpt from the O'Reilly book "Getting Started With Bluetooth Low Energy":

To enable notifications on Android, you normally have to locally enable the notification for the particular characteristic you are interested in.

Once that’s done, you also have to enable notifications on the peer device by writing to the device’s client characteristic configuration descriptor (CCCD)

I believe this answers your question.

So

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); 

refers to the first part

and

mBluetoothGatt.writeDescriptor(descriptor); 

refers to the 2nd.

stkent
  • 19,772
  • 14
  • 85
  • 111
Tomi
  • 158
  • 1
  • 6
  • 4
    So it's a 2 step process. The BLE device being configured to notify on new data available, and the Android app interrupting itself to process the data. The flexibility allows for the android device to ignore notifications form the BLE device when it doesn't want to and re-enable when it needs to start processing data. It might look cumbersome from a code point, but I think it offers more flexibility. – Tomi Aug 13 '16 at 17:11
  • 4
    Just to add more clarity, you can enable notifications locally and also remotely on the BLE device. I get your point that why would you ever want to do either and not both, but there are times when you temporarily want to disable receiving notifications without actually having to write to the remote device. You can just disable locally. – Tomi Aug 19 '16 at 10:36
4

For future peoples coming across this, here's the best answer I could find:

By writing to the Client Characteristic Config descriptor, you, the client, are telling the BLE server to switch configurations. (This made no sense to me either initially, but in english:)

This tells the BLE device to switch modes (configurations) to actively gather and report changes to this characteristic instead of changing and only reporting when requested.

It's poorly named, but digging through the docs it appears this is also going to be used for other possible characteristic changes that the client might request: hence the confusing name.

https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml

This begs the question, why call BluetoothGatt.setCharacteristicNotification() if we're only going to duplicate our efforts by modifying the descriptor?! Digging through the BluetoothGatt source shows us that setCharacteristicNotification only prepares the local service to receive notifications, not to enable persistent updates.

Michael Powell
  • 746
  • 4
  • 11
  • 1
    That still doesn't answer the question *why* it doesn't set the CCCD. In what situtation would you want to enable notifications on the peripheral and not the central, or vice versa? – Timmmm Apr 20 '16 at 08:28
  • A bit late to the party but... @Timmmm - it doesn't make sense unless it is an Android implementation issue such as needing to allocate more buffers or something similar. For sure, the only way to get a remote devices to send notifications or indications is to write the appropriate value to the CCCD so this setCharacteristicNotification() has nothing to do with Bluetooth per se. Compare this with how iOS does it - a single call. – Andrew Coad Sep 22 '21 at 09:42
1

I know it looks silly, but setting CCCD value is the only way you can tell the API whether you are going to turn on notification or indication.

Currently there is no setCharacteristicIndication. To enable indication, you have to call setCharacteristicNotification (confusing) and then write BluetoothGattDescriptor.ENABLE_INDICATION_VALUE to CCCD, similar to what you did to enable Notification.

reTs
  • 1,808
  • 1
  • 13
  • 26
  • So what does `setCharactersticNotification()` actually *do*? I.e. what will happen if you don't call it but still write `ENABLE_NOTIFICATION_VALUE` to the CCCD? – Timmmm Apr 17 '14 at 09:19
  • Didn't really dig deep into the source, but I think it just tell the Bluetooth stack to read CCCD value. The function name is really confusing. – reTs Apr 17 '14 at 11:23
  • 1
    I've struggled with this question a bit as well and my guess (without reading the source) is this: when notifications start coming in from the BLE device, the Android device doesn't know where to route those notifications. Does it go to your app? or another app? or no app? I think enabling/disabling characteristic notifications is a way of telling the OS "my app wants these notifications. send them to this app." If you're wondering why that wouldn't be done automatically, its probably because there are cases where you are enabling notifications for other devices to receive them instead. – SuperDeclarative Oct 08 '15 at 08:39
  • 1
    Oh and another possible reason is the reverse of what I just mentioned. What if the notifications are already being broadcast? You shouldn't have to enable them a 2nd time on the BLE device, so instead you simply setCharacteristicNotification() and start receiving them immediately. – SuperDeclarative Oct 08 '15 at 08:42
0

All the other answers do not really answer the question.

My guess would be that the Android BLE team took the assumption an application could have more than one BluetoothGattCallback().

By splitting the notification enabling (and disabling) in two steps it would allow the BluetoothGattCallback observers to listen for GATT notification (only invoke setCharacteristicNotification()) - and leave only one BluetoothGattCallback implemention doing the write operations to the GATT server aka the BLE peripheral.

OlivierM
  • 2,820
  • 24
  • 41
-4

"The Client Characteristic Configuration descriptor defines how the characteristic may be configured by a specific client."

Alan Xue
  • 1
  • 1