I am using BluetoothAdapter.startDiscovery() to find a specific Bluetooth device (it is Bluetooth 2.0 device, so I have to use BluetoothAdapter.startDiscovery()). I have all required permissions (Bluetooth, Location), and the app works just fine.
But now I have to use this app under Android Enterprise (Work Profile). Under Work Profile it does not start scan. All the limitations in the profile are disabled.
After investigation logcat I found such lines:
Non work profile log:
2019-07-29 10:23:13.109 10230-10446/? D/BluetoothAdapterService: startDiscovery() uid = 10126, pid = 6328
2019-07-29 10:23:13.110 10230-10446/? D/BluetoothAdapterService: startDiscovery
Work profile log:
2019-07-29 10:21:26.536 10230-13473/? D/BluetoothAdapterService: startDiscovery() uid = 1010126, pid = 13390
2019-07-29 10:21:26.536 10230-13473/? W/BluetoothAdapterService: startDiscovery() - Not allowed for non-active user
This is the log from OnePlus 6, but I observed similar logcat also on Samsung S10. Also customers said they have same problem on S9, S8 and on some Nokia device (actually all devices they tested on).
Searching through Android sources, I found the following code, producing this log:
public boolean startDiscovery() {
if (!Utils.checkCaller()) {
Log.w(TAG, "startDiscovery() - Not allowed for non-active user");
return false;
}
AdapterService service = getService();
if (service == null) return false;
return service.startDiscovery();
}
Which calls Utils.checkCaller() to check:
public static boolean checkCaller() {
boolean ok;
// Get the caller's user id then clear the calling identity
// which will be restored in the finally clause.
int callingUser = UserHandle.getCallingUserId();
int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
// With calling identity cleared the current user is the foreground user.
int foregroundUser = ActivityManager.getCurrentUser();
ok = (foregroundUser == callingUser);
if (!ok) {
// Always allow SystemUI/System access.
int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
"com.android.systemui", UserHandle.USER_OWNER);
ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid);
}
} catch (Exception ex) {
Log.e(TAG, "checkIfCallerIsSelfOrForegroundUser: Exception ex=" + ex);
ok = false;
} finally {
Binder.restoreCallingIdentity(ident);
}
return ok;
}
So startDiscovery() uid = 10126 and startDiscovery() uid = 1010126 differ in uid, which I think is caused by work profile, which as I think causes the check (foregroundUser == callingUser) to be false.
Finally the question: Am I right that it is a bug? If yes - is there any way to workaround it? If no - how can I enable Bluetooth classic scan in work profile?
I'm creating a custom BLE service on Android with a single characteristic that can be read/written. The code looks like this:
public static UUID MY_SERVICE = UUID.fromString("e0ec8d9c-5e4d-470a-b87f-64f433685301");
public static UUID MY_CHARACTERISTIC = UUID.fromString("e0ec8d9c-5e4d-470a-b87f-64f433685302");
/**
* Return a configured {#link BluetoothGattService} instance for the
* Custom Service.
*/
public static BluetoothGattService createCustomBleService() {
BluetoothGattService service = new BluetoothGattService(MY_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Current Configuration characteristic
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(MY_CHARACTERISTIC,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ |BluetoothGattCharacteristic.PERMISSION_WRITE);
boolean serviceAdded = service.addCharacteristic(characteristic);
Log.i(TAG, "Building BLE service addCharacteristic returned "+serviceAdded);
return service;
}
The call to addCharacteristic(...) returns true. The service itself is created, can be advertised, and the service and its characteristic are discoverable by clients. Somewhere else in client side code, subsequent to a BLE scan that locates said service, the discovery code that runs looks like this:
for (BluetoothGattService service : gatt.getServices()) {
serviceUuid = service.getUuid().toString();
if( MY_SERVICE.toString().equals(serviceUuid) ) {
List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
for( BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics ) {
characteristicUuid = gattCharacteristic.getUuid().toString();
Log.d(TAG, "onServicesDiscovered() - found characteristic uuid="+characteristicUuid);
int cProps = gattCharacteristic.getProperties();
Log.d(TAG, "onServicesDiscovered() - found characteristic properties "+cProps);
if ((( MY_CHARACTERISTIC.toString().equals(characteristicUuid) ))&&((cProps & BluetoothGattCharacteristic.PROPERTY_WRITE)>0)) {
writeCharacteristic(gatt,gattCharacteristic,"configStringLiteral");
}
}
}
}
When this service discovery code runs, as I mentioned, it finds the custom service and the characteristic that were defined. Any values I set for characteristic properties show up properly at the time of discovery on client side. The characteristic shows as writeable.
The problem is that characteristic write fails always even though the properties say its writable.
Has anyone seen this?... or perhaps I'm doing something dumb and have been looking at it too long.
(BTW the device hosting the custom service at runtime is a Samsung Galaxy 7 and the client is a Galaxy 6 ...or vice versa, same behavior)
Permission info is not sent over BLE when the services are discovered. Therfore the permission property should not be used for remote characteristics.
A client should inspect the characteristic property only to decide what can be done. If it receives an error saying for example encryption needed, the Bluetooth stack shall start encryption and then retry the request.
So, characteristic properties are the correct way to declare to a client what can be done with it. Characteristic permissions only tell the local GATT Server implementation how it should react to incoming GATT requests.
I'm developing an Android application which opens a BLE connection between the Android device and a BLE pheripheral (a simple transmitter).
The peripheral is programmed to have multiple reading characterics which I found.
The problem shows up when I try to enable the notification.
The first always returns true, and than it starts to trigger my notify callback, the others always return a false value.
List<BluetoothGattDescriptor> descrittoriDellaChar = getListaDescrittoriDaCharact(charact);
Boolean status = null;
for (int i = 0; i < descrittoriDellaChar.size(); i++) {
BluetoothGattDescriptor TargetDescriptor = descrittoriDellaChar.get(i);
byte[] valore = TargetDescriptor.getValue();
if (valore != BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) {
getCharDiLettura().add(charact);
TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
boolean success=false;
while(success==false) {
success = gattGlobale.writeDescriptor(TargetDescriptor);
}
status = gattGlobale.setCharacteristicNotification(charact, true);
}
}
boolean prio= gattGlobale.requestConnectionPriority(gattGlobale.CONNECTION_PRIORITY_HIGH);
I was using the same method since I had just 1 characteristic to read, and now it doesn't work anymore.
Sending read and write requests one after one other synchronously does not work since android only allows one pending GATT operation at a time (that's the reason it returns false). You must somehow enqueue the work and continue sending the next request once the callback (onCharacteristicRead/onCharacteristicWrite/onDescriptorWrite) of the previous request arrives.
has anyone tried using HM-10 Bluetooth module?
I'm able to pair with it using an Android device and passing the pre-defined PIN. Based on the UART return, the pairing is successful (module returns OK+CONN - means a connection was established)
However, after a few seconds (2-3), the UART receives OK+LOST; means the connection was lost. Also, the LED starts blinking (normally, when a connection is active, it stays lit)
Is this normal behaviour for bluetooth in general or the HM-10 module.
This is the product's website: http://www.jnhuamao.cn/bluetooth.asp?ID=1
I'm not sure, but HM -10 don't support rfcom. It's mean that you must use GATT functionality for communication. Entity of BLE is usage of minimum data package as it possible, so BLE don't hold the connection all times and use something like statuses [attributes].
So, few code lines for example, how work with BLE:
1.
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(DEVICE_ADDR);
That's device initiation, the same like with simple bluetooth, where DEVICE_ADDR is the MAC of your BLE(how to find this address you can find in google or stack overflow, its trivial)
2.
BluetoothGattService mBluetoothGattService;
BluetoothGatt mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> gattServices = mBluetoothGatt.getServices();
for(BluetoothGattService gattService : gattServices) {
if("0000ffe0-0000-1000-8000-00805f9b34fb".equals(gattService.getUuid().toString()))
{
mBluetoothGattService = gattService;
}
}
} else {
Log.d(TAG, "onServicesDiscovered received: " + status);
}
}
};
So, what this code mean: if u can see from this part of code, i describe how GATT service find. This service needed for "attribute" communication. gattService.getUuid() has few uuids for communication(4 in my module), some of them used for RX, some for TX etc. "0000ffe0-0000-1000-8000-00805f9b34fb" that is one of uuid that use for communication thats why i check it.
The final part of code is message sending:
BluetoothGattCharacteristic gattCharacteristic = mBluetoothGattService.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
String msg = "HELLO BLE =)";
byte b = 0x00;
byte[] temp = msg.getBytes();
byte[] tx = new byte[temp.length + 1];
tx[0] = b;
for(int i = 0; i < temp.length; i++)
tx[i+1] = temp[i];
gattCharacteristic.setValue(tx);
mBluetoothGatt.writeCharacteristic(gattCharacteristic);
After sending message contain hold on and you can send another message or can close connection.
More info, you can find on https://developer.android.com/guide/topics/connectivity/bluetooth-le.html.
PS: MAC address of your module can find with ble scanner code or with AT cmd:
on my firmware AT+ADDR or AT+LADDR
About UUIDs usage: not sure, but in my case, i find it with next AT+UUID [Get/Set system SERVER_UUID] -> Response +UUID=0xFFE0, AT+CHAR [Get/Set system CHAR_UUID] - Response +CHAR=0xFFE1. Thats why i make conclusion that UUID which i must use fe "0000[ffe0/is 0xFFE0 from AT response]-0000-1000-8000-00805f9b34fb"
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?
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?
}
#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());
}
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.
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.
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.
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.
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);
}
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;
}
}
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.
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