76

Device used for testing: Nexus 4, Android 4.3

Connection is working fine but the onCharacteristicChangedMethod of my callback is never called. However I am registering for notifications using setCharacteristicNotification(char, true) inside onServicesDiscovered and that function even returns true.

Device log (there are actually no messages at all when notifications should appear / are sent via the Bluetooth device):

07-28 18:15:06.936  16777-16809/de.ffuf.leica.sketch D/BluetoothGatt: setCharacteristicNotification() - uuid: 3ab10101-f831-4395-b29d-570977d5bf94 enable: true
07-28 18:15:06.936    4372-7645/com.android.bluetooth D/BtGatt.GattService: registerForNotification() - address=C9:79:25:34:19:6C enable: true
07-28 18:15:06.936    4372-7645/com.android.bluetooth D/BtGatt.btif: btif_gattc_reg_for_notification
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1018
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.GattService: onRegisterForNotifications() - address=null, status=0, registered=1, charUuid=3ab10101-f831-4395-b29d-570977d5bf94
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1016
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1018
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.GattService: onRegisterForNotifications() - address=null, status=0, registered=1, charUuid=3ab10102-f831-4395-b29d-570977d5bf94
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1016
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1013
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1013
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.976    4372-7645/com.android.bluetooth D/BtGatt.btif: btif_gattc_upstreams_evt: Event 9

GATT Notifications work fine using iOS and the app basically does the same as on Android (registering for notification etc.).

Has anyone else experienced this with a possible solution?

OneWorld
  • 17,512
  • 21
  • 86
  • 136
Boni2k
  • 3,255
  • 3
  • 23
  • 27

10 Answers10

86

It seems like you forgot to write the Descriptor which tells your BLE device to go in this mode. See the code lines that deal with descriptor at http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#notification

Without setting this descriptor, you never receive updates to a characteristic. Calling setCharacteristicNotification is not enough. This is a common mistake.

code snipped

protected static final UUID CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

public boolean setCharacteristicNotification(BluetoothDevice device, UUID serviceUuid, UUID characteristicUuid,
        boolean enable) {
    if (IS_DEBUG)
        Log.d(TAG, "setCharacteristicNotification(device=" + device.getName() + device.getAddress() + ", UUID="
                + characteristicUuid + ", enable=" + enable + " )");
    BluetoothGatt gatt = mGattInstances.get(device.getAddress()); //I just hold the gatt instances I got from connect in this HashMap
    BluetoothGattCharacteristic characteristic = gatt.getService(serviceUuid).getCharacteristic(characteristicUuid);
    gatt.setCharacteristicNotification(characteristic, enable);
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
    descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : new byte[] { 0x00, 0x00 });
    return gatt.writeDescriptor(descriptor); //descriptor write operation successfully started? 
}
Anthony Grist
  • 38,173
  • 8
  • 62
  • 76
OneWorld
  • 17,512
  • 21
  • 86
  • 136
  • I have just tried the code from Android 4.3 examples but failed to notify. Could you please share the piece of codes related to notification? Thank you. – ZhangChn Aug 06 '13 at 06:48
  • I've done that, too, even with your method (which returns true). Still no luck. I'm using the Descriptor UUID (descUuid) I found in logcat on the line D/BtGatt.GattService: onGetDescriptor() - address=C9:79:25:34:19:6C, status=0, descUuid=00002902-0000-1000-8000-00805f9b34fb – Boni2k Aug 06 '13 at 08:24
  • The UUID you found is the same as the one I am using. It's actually a UUID within the BLE standard. Check, if you get error status codes (other than 0) in your callbacks. E.g. connect(), onDescriptorWrite() – OneWorld Aug 06 '13 at 09:03
  • @Boni2k It seems a stupid question, but did you actually confirm that the characteristic has the descriptor 0x2902(using 16-bit here for convenience) – reTs Aug 07 '13 at 14:19
  • @reTs Your characteristic has to offer this descriptor (0x2902) to enable characteristic update notifications. – OneWorld Aug 07 '13 at 15:08
  • @OneWorld I know, I am just asking the OP to confirm that this descriptor really exists in the characteristic. – reTs Aug 07 '13 at 15:35
  • @OneWorld: Thanks for the hint. Indeed I am getting status 128 inside onDescriptorWrite, which indicates an error. Although I have no idea what that status code could mean... – Boni2k Aug 19 '13 at 13:03
  • 2
    @Boni2k GATT_NO_RESOURCES = -128 or 128; GATT_INTERNAL_ERROR = -127 or 129; GATT_ERROR = -123 or 133; GATT_ALREADY_OPEN = -115 or 141. I got them from the Samsung BLE Sdk guys. Those status codes seem to be equal to the Android BLE SDK except for having signed byte as type. The whole list is here: http://img-developer.samsung.com/onlinedocs/samsung_ble_docs_200/index.html – OneWorld Aug 19 '13 at 13:14
  • Thanks. FWIW, here's the stupid mistake I made: ENABLE_INDICATION_VALUE had to be used instead of ENABLE_NOTIFCATION_VALUE... – Boni2k Sep 24 '13 at 06:50
  • hmmm I tried this but still no updates, whether I set ENABLE_NOTIFICATION_VALUE or ENABLE_INDICATION_VALUE. Do you know how iOS does this? My device works fine in iOS and you don't have to write any descriptors, so I'm wondering if they do it in the background or something? – phreakhead Dec 14 '13 at 05:34
  • @OneWorld Do you know where to actually find this UUID in the BLE standard? Could you maybe provide us with an url? Thanks in advance! – Brabbeldas Dec 03 '14 at 13:25
  • @Brabbeldas Sorry, I just can help you by providing the right search terms: The UUID of the descriptor is a 16bit UUID which also can be named "2902". You'll find plenty of docs as PDF on https://www.bluetooth.org/en-us/specification/adopted-specifications and browseable HTML content on https://developer.bluetooth.org/Pages/default.aspx – OneWorld Dec 04 '14 at 08:32
  • 1
    I am seeing that I have to induce a delay (ex: Thread.sleep, or Handler.post) between the setCharacteristicNotification and the writeDescriptor. If I don't, I never get the onCharacteristicChanged. Can anyone confirm that they see something similar? Note that the example at http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#notification has a subtle "..." between the two calls, which I am seeing indicates that you can't reliably always do these two calls immediately back to back. – swooby Apr 07 '15 at 19:46
  • If my previous comment is true: I don't see that "setCharacteristicNotification" itself has any callback, so can anyone think of any way other than a delay before calling writeDescriptor that can make this more reliable? – swooby Apr 07 '15 at 19:50
  • I think it may have nothing to do with the delay *between* the commands; i think is it just the delay in general before any attempt to set the notification is even started. Looking at the code for setCharacteristicNotification, it doesn't appear to have the "only one gatt request at a time" limitation. – swooby Apr 07 '15 at 20:18
  • 3
    It's not a mistake, it's a bug in the API. The whole point of setCharacteristicNotification is to enable notifications--*it* should be setting this. – Glenn Maynard May 18 '15 at 15:16
  • 7
    These additional necessary steps should really be documented. There are a too many and too heavy undocumented caveats that developers need to be aware of when implementing BLE communication on Android. – Lars Blumberg Nov 20 '15 at 16:09
  • We're working closely with Google on something similar... not entirely sure if it is identical to your issue, but we found that when Google's OS does the gatt.WriteDescriptor(), they're sending it as a WRITE_CMD and not a WRITE_REQUEST. I believe, although I could be mistaken, that they're planning to change it so that it does BOTH. We worked around this issue in the meantime by responding to either CMD or REQUEST for that message, however it requires us to update the firmware in our device.. and our customers need something other than android to do that over BLE or a USB cable – Jason Nelson Dec 04 '15 at 21:49
  • Wow, I cannot believe that I am revisiting this issue over 9 months later. I am recently seeing, and had occasionally seen in the past, that "characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID)" is returning null. if the descriptor is null then the given sequence is unusable. Can anyone think of what causes that to happen, or if there could be any workaround? – swooby Jan 27 '16 at 01:02
  • @swooby Maybe try to scan the services again when this happens. – racs May 08 '16 at 05:28
  • What do you mean by mGattInstances how can I get this instance? – mesopotamia Jun 15 '16 at 06:54
41

@Boni2k - I have the same issues. In my case, I have 3 notifying characteristics and a handful of read/write characteristics.

What I did find is that there is some dependency between writeGattDescriptor and readCharacteristic. All of the writeGattDescriptors must come first and complete before you issue any readCharacteristic calls.

Here is my solution using Queues. Now I am getting notifications and everything else works fine:

Create two Queues like this:

private Queue<BluetoothGattDescriptor> descriptorWriteQueue = new LinkedList<BluetoothGattDescriptor>();
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();

Then write all of your descriptors immediately after discovery with this method:

public void writeGattDescriptor(BluetoothGattDescriptor d){
    //put the descriptor into the write queue
    descriptorWriteQueue.add(d);
    //if there is only 1 item in the queue, then write it.  If more than 1, we handle asynchronously in the callback above
    if(descriptorWriteQueue.size() == 1){   
        mBluetoothGatt.writeDescriptor(d);      
    }
}

and this callback:

public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {         
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "Callback: Wrote GATT Descriptor successfully.");           
        }           
        else{
            Log.d(TAG, "Callback: Error writing GATT Descriptor: "+ status);
        }
        descriptorWriteQueue.remove();  //pop the item that we just finishing writing
        //if there is more to write, do it!
        if(descriptorWriteQueue.size() > 0)
            mBluetoothGatt.writeDescriptor(descriptorWriteQueue.element());
        else if(readCharacteristicQueue.size() > 0)
            mBluetoothGatt.readCharacteristic(readQueue.element());
    };

The method for reading a characteristic normally then looks like this:

public void readCharacteristic(String characteristicName) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(kYourServiceUUIDString));
    BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(characteristicName));
    //put the characteristic into the read queue        
    readCharacteristicQueue.add(c);
    //if there is only 1 item in the queue, then read it.  If more than 1, we handle asynchronously in the callback above
    //GIVE PRECEDENCE to descriptor writes.  They must all finish first.
    if((readCharacteristicQueue.size() == 1) && (descriptorWriteQueue.size() == 0))
        mBluetoothGatt.readCharacteristic(c);              
}

and my read callback:

public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        readCharacteristicQueue.remove();
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);                                
        }
        else{
            Log.d(TAG, "onCharacteristicRead error: " + status);
        }

        if(readCharacteristicQueue.size() > 0)
            mBluetoothGatt.readCharacteristic(readCharacteristicQueue.element());
    }
Paulo Tomé
  • 1,910
  • 3
  • 18
  • 27
miznick
  • 419
  • 3
  • 4
  • 4
    Take care of the synchronous nature of the BLE implementation in Android. You easily can cancel requests unwillingly, when not using such queues as you do. See http://stackoverflow.com/questions/18011816/has-native-android-ble-gatt-implementation-synchronous-nature?rq=1 I'm not sure, if you are right with your assumption, that writeDescriptor() needs to be done prior readCharacteristic. Maybe your solution (queues) just takes care of the synchronous nature and fixes your problem that way. I actually do read and write characteristics before writing the descriptor. – OneWorld Aug 19 '13 at 15:19
  • I do believe you are correct. The transactions are synchronous and the order is irrelevant. Any transaction must complete it's callback before another transaction is issued and everything will work properly. Might make sense to use one queue for all transactions and just tag each one with a type (write descriptor, read characteristic, write characteristic, etc.) – miznick Aug 21 '13 at 11:05
  • your code is good to go , but here i am facing some problem to use this, their are some issues i am facing because i am new to BLE. Mean on readCharacteristicQueue and readQueue i am getting erros. Please attach some complete code to get rid of this error. Thanks – Deepak Jan 30 '15 at 06:49
  • Code looks ok in theory but sometimes I don't get onCharacteristicWrite callback so... – Thomas Feb 06 '15 at 15:54
  • 1
    Nordic have written a class to handle this called `GattManager`. See https://github.com/NordicSemiconductor/puck-central-android – Timmmm Mar 20 '15 at 13:28
  • This quite nice Nordic GattManager code does an odd "Thread.sleep(1000)" in its GattSetNotificationOperation code (https://github.com/NordicSemiconductor/puck-central-android/blob/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt/operations/GattSetNotificationOperation.java#L38). Is this sleep really necessary? I am seeing that if I comment out the sleep, I don't get the onCharacteristicChanged. – swooby Apr 07 '15 at 19:44
  • I think it may have nothing to do with the delay *between* the commands; i think is it just the delay in general before any attempt to set the notification is even started. Looking at the code for setCharacteristicNotification, it doesn't appear to have the "only one gatt request at a time" limitation. – swooby Apr 07 '15 at 20:18
  • I found that after service discovery, a characteristic will have the 0x2902 "notify" descriptor attached as expected. However if I go and write some other characteristic first, that descriptor no longer appears in the `getDescriptors()` list and returns null. If, however, I locate the notify BluetoothGattDescriptor in `onServicesDiscovered()` and save it for later, I am able to come back to it later, set its value to enable notifications, and write the descriptor. – Tom K Feb 26 '19 at 09:29
  • Isn't there a case where you have 2 items in your readqeue and while it's doing a read, we add an item to the writeDescriptor queue, and then we are writing to the descriptor at the same time as reading from the readqueue? – Chase Roberts Feb 27 '19 at 18:17
10

When setting the value to the descriptor instead of putting descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE), put descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE). The callbacks for onCharacteristicChanged are called now.

Bucket
  • 7,415
  • 9
  • 35
  • 45
user2926265
  • 117
  • 1
  • 2
  • Can you clarify what "indication" is supposed to do? Based on the docs, I was sure that the right flag to set in this case was ENABLE_NOTIFICATION_VALUE but you're now the second person on this page who has suggested that ENABLE_INDICATION_VALUE is the right flag. What is the difference and use case for each? – jkraybill Oct 28 '13 at 07:56
  • 2
    It depends on the Bluetooth Profile implementation inside the bluetooth device. It either uses "Notification" or "Indication" for posting updates. So you'll have to find out which one your device uses. – Boni2k Oct 28 '13 at 20:47
  • 1
    Like Boni2k said, it depends on the Bluetooth Profile. If your service follows the standard profiles you should be able to find that information [here] (https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx). If you want to know this programatically, you can call the getProperties() method for your characteristic and do a bitwise AND against the property you want to check if it's different than 0, then it supports that activity. – user2926265 Nov 01 '13 at 14:23
  • What bothers me here is that the Android documentation states that setCharacteristicNotifications works for EITHER notifications or indications. So I assume they are checking the properties internally and writing the correct flags to the characteristic. They also use the method in their example which has worked with some HeartRate monitors. Do you know if the method is flawed? My understanding is that one cannot set BOTH indication/notification flags though some devices may accept that. IF they are doing that then I would say their method IS wrong. – Brian Reinhold Apr 02 '14 at 09:48
8

I assume (you did not provide your source code) that you did not implement it as Google wanted:

(1)

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

and then

(2)

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

I suppose 2 is missing. In that case I believe on low-level notification will be triggered but they will never be reported to application layer.

Marian Paździoch
  • 8,813
  • 10
  • 58
  • 103
6

Experienced issues in earlier versions of Android receiving notifications (an indication that was registered) and always had a strange disconnect event afterwards. As it turns out, this was because we registered for notifications on five characteristics.

The error discovered in LogCat was:

02-05 16:14:24.990    1271-1601/? E/bt-btif﹕ Max Notification Reached, registration failed.

Prior to 4.4.2, the number of registrations was capped at 4! 4.4.2 increased this limit to 7.

By reducing the number of registrations in earlier versions, we were able to step around this limitation.

stkent
  • 19,772
  • 14
  • 85
  • 111
Dan Wuensch
  • 104
  • 1
  • 4
4

Well, this API name surely lead some confusions to app developer if he/she was not the Bluetooth background programmer.

From Bluetooth core specification perspective, quote from core spec 4.2 Vol 3, Part G section 3.3.3.3 "Client Characteristic Configuration" :

The characteristic descriptor value is a bit field. When a bit is set, that action shall be enabled, otherwise it will not be used.

and section 4.10

Notifications can be configured using the Client Characteristic Configuration descriptor (See Section 3.3.3.3).

which is clearly states that if client want to receive the notification(or indication,which need response) from server, should write the "Notification" bit to 1("Indication" bit also to 1 otherwise).

However, the name "setCharacteristicNotification" give us a hint is that if we set the parameters of this API as TURE, the client would got notifications; unfortunately this API only set the local bit to allow the notification sent to apps in case of remote notification comes. See code from Bluedroid:

    /*******************************************************************************
    **
    ** Function         BTA_GATTC_RegisterForNotifications
    **
    ** Description      This function is called to register for notification of a service.
    **
    ** Parameters       client_if - client interface.
    **                  bda - target GATT server.
    **                  p_char_id - pointer to GATT characteristic ID.
    **
    ** Returns          OK if registration succeed, otherwise failed.
    **
    *******************************************************************************/

    tBTA_GATT_STATUS BTA_GATTC_RegisterForNotifications (tBTA_GATTC_IF client_if,
                                                         BD_ADDR bda,
                                                         tBTA_GATTC_CHAR_ID *p_char_id)

{
    tBTA_GATTC_RCB      *p_clreg;
    tBTA_GATT_STATUS    status = BTA_GATT_ILLEGAL_PARAMETER;
    UINT8               i;

    if (!p_char_id)
    {
        APPL_TRACE_ERROR("deregistration failed, unknow char id");
        return status;
    }

    if ((p_clreg = bta_gattc_cl_get_regcb(client_if)) != NULL)
    {
        for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++)
        {
            if ( p_clreg->notif_reg[i].in_use &&
                 !memcmp(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN) &&
                  bta_gattc_charid_compare(&p_clreg->notif_reg[i].char_id, p_char_id))
            {
                APPL_TRACE_WARNING("notification already registered");
                status = BTA_GATT_OK;
                break;
            }
        }
        if (status != BTA_GATT_OK)
        {
            for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++)
            {
                if (!p_clreg->notif_reg[i].in_use)
                {
                    memset((void *)&p_clreg->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG));

                    p_clreg->notif_reg[i].in_use = TRUE;
                    memcpy(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN);

                    p_clreg->notif_reg[i].char_id.srvc_id.is_primary = p_char_id->srvc_id.is_primary;
                    bta_gattc_cpygattid(&p_clreg->notif_reg[i].char_id.srvc_id.id, &p_char_id->srvc_id.id);
                    bta_gattc_cpygattid(&p_clreg->notif_reg[i].char_id.char_id, &p_char_id->char_id);

                    status = BTA_GATT_OK;
                    break;
                }
            }
            if (i == BTA_GATTC_NOTIF_REG_MAX)
            {
                status = BTA_GATT_NO_RESOURCES;
                APPL_TRACE_ERROR("Max Notification Reached, registration failed.");
            }
        }
    }
    else
    {
        APPL_TRACE_ERROR("Client_if: %d Not Registered", client_if);
    }

    return status;
}'

so what matters was the descriptor write action.

stkent
  • 19,772
  • 14
  • 85
  • 111
Guo Xingmin
  • 1,013
  • 1
  • 7
  • 7
2

This one is working for me:

to notify master device that some characteristic is change, call this function on your pheripheral:

private BluetoothGattServer server;
//init....

//on BluetoothGattServerCallback...

//call this after change the characteristic
server.notifyCharacteristicChanged(device, characteristic, false);

in your master device: enable setCharacteristicNotification after discover the service:

@Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        services = mGatt.getServices();
        for(BluetoothGattService service : services){
            if( service.getUuid().equals(SERVICE_UUID)) {
                characteristicData = service.getCharacteristic(CHAR_UUID);
                for (BluetoothGattDescriptor descriptor : characteristicData.getDescriptors()) {
                    descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                    mGatt.writeDescriptor(descriptor);
                }
                gatt.setCharacteristicNotification(characteristicData, true);
            }
        }
        if (dialog.isShowing()){
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    dialog.hide();
                }
            });
        }
   }

now you can check your characteristic value is change, for example onCharacteristicRead function (this also working on onCharacteristicChanged function as well) :

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Log.i("onCharacteristicRead", characteristic.toString());
        byte[] value=characteristic.getValue();
        String v = new String(value);
        Log.i("onCharacteristicRead", "Value: " + v);
}
Doni
  • 576
  • 6
  • 9
1

Here's a simple way to do it, but let me know if you see any drawbacks.

Step 1 Declare boolean variables

private boolean char_1_subscribed = false;
private boolean char_2_subscribed = false;
private boolean char_3_subscribed = false;

Step 2 subscribe to the first characteristic in the onServicesDiscovered callback:

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
    } else {
        Log.w(TAG, "onServicesDiscovered received: " + status);
    }
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if(!char_1_subscribed)
        subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_1)); char_1_subscribed = true;
}

Step 3

Subscribe to any others after the onCharacteristicChanged callback fires

@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
                                    BluetoothGattCharacteristic characteristic) {
    if(UUID_CHAR_1.equals(characteristic.getUuid()))
    {
        if(!char_1_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_2)); char_2_subscribed = true;
    }
    if(UUID_CHAR_2.equals(characteristic.getUuid()))
    {
        if(!char_3_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_3)); char_3_subscribed = true;
    }
}
JBaczuk
  • 13,886
  • 10
  • 58
  • 86
0

I had another reason that I would like to add as it drove me crazy the whole day:

On my Samsung Note 3 I did not receive notifications of changed values while the same code worked on any other device I tested with.

Rebooting the device solved all the problems. Obvious, but when you are in the problem, you forget to think of.

Christian
  • 4,596
  • 1
  • 26
  • 33
-1

I've experienced the problems with notifications for BLE on Android as well. However there's a fully working demo that includes a bluetooth wrapper around BluetoothAdapter. The wrapper is called BleWrapper and ships with the demo application called BLEDemo contained in the Application Accelerator package. Download here: https://developer.bluetooth.org/Pages/Bluetooth-Android-Developers.aspx. You need to register with your email address at the top right before downloading. The project's license allows for free use, code modification and publication.

To my experience the Android demo application handles BLE notification subscriptions very well. I've not yet dived too much into the code to see how the wrapper actually wraps.

There's an Android app available in Play Store that is a customization of the Application accelerator demo. As the user interface looks nearly the same I suppose that it also uses BleWrapper. Download the app here: https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner

Lars Blumberg
  • 19,326
  • 11
  • 90
  • 127