26

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

6 Answers 6

17

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

2
  • 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, 2014 at 8: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, 2023 at 5:46
15

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.

2
  • 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, 2016 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, 2016 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.

2
  • 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, 2016 at 8: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. Sep 22, 2021 at 9: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.

4
  • 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, 2014 at 9: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, 2014 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. Oct 8, 2015 at 8: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. Oct 8, 2015 at 8: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.

-4

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

2
  • That doesn't answer the question.
    – Timmmm
    Oct 6, 2015 at 9:16
  • I'd give you an upvote for disrupting my continuous frustration regarding this subject. Nov 23, 2018 at 12:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.