Android Central with GattServer - android

I'm trying to program Bluetooth low energy - AlertNotificationService (ANS). It is a bit weird service because typically services run on GAP peripheral. But ANS run on GAP central. So typical way of working shall be for example this:
Watch - GAP peripheral (broadcasting), GATT client
Phone - GAP central , GATT server
Basically it works for me but not always. And this instability is very strange to me. When I look with bluetooth LE analyzer I see that Android GATT server sometimes tells there are no characteristic in my profile...
It looks like this: "watch" ask for my GATT service (I know it is proprietary not ANS UUID)
Slave->Master ATT Rcvd Find By Type Value Request, GATT Primary Service Declaration 11:22:33:44:11:22:33:44:11:76:62:65:01:00:00:00
Phone says the service is there starting from handle 0x35
Master->Slave ATT Rcvd Find By Type Value Response Handle: 0x0035
Watch ask for charactersitisc from handle 0x35
Slave->Master ATT Rcvd Read By Type Request, GATT Characteristic Declaration, Handles: 0x0035..0xffff
But phone sometimes incorrectly says that there is no characteristic from that handle:
Master->Slave ATT Rcvd Error Response - Attribute Not Found, Handle: 0x0035
When I do add service and characteristic into GATT server I always get "true" from functions. I do it like this:
BluetoothGattService service =new BluetoothGattService(Vbe_AnsExt.UUID_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characApp =
new BluetoothGattCharacteristic(Vbe_AnsExt.UUID_CharacApp,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic characMsg =
new BluetoothGattCharacteristic(Vbe_AnsExt.UUID_CharacMsg,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ );
boolean ret;
ret = service.addCharacteristic(characApp);
Log.i("vbeInit_ASN_Ext_Server","addCharApp retruned: "+ret);
ret = service.addCharacteristic(characMsg);
Log.i("vbeInit_ASN_Ext_Server","addCharMsg retruned: "+ret);
ret = mGattServer.addService(service);
Log.i("vbeInit_ASN_Ext_Server","addService retruned: "+ret);
Any idea what might be the problem? I noticed that there is function BluetoothGattServer::connect(). I'm not sure how to use it. I'm using standard BluetoothDevice::connectGatt(). But I guess if I would do something wrong it shall work never - not sometimes...
I'm using Android M (6.0.1) on Samsung SM-G920F.
[UPDATE]
I noticed that after phone restart it always works. After application close and reopen it usually does not works. It does not works in different ways ->
sometimes no characteristic is found;
sometimes only first characteristic is found
sometimes only last(second) characteristic is found.
When I start Gatt Server I do it like this:
mGattServer = bluetoothManager.openGattServer(appContext, mGattServerCallback);
mGattServer.clearServices();
When I close app (onDestroy()) I do close gattserver:
mGattServer.close();
I also tried not to close the GATT server, but it did not help. Any ideas what might get wrong between closing and reopening?

So I may have found the reason. I'm adding 2 services: ANS and my service ANS_extension. It seems it helps when I wait after adding the service on call back onServiceAdded()
So now it looks like this:
public static UUID UUID_SupportedNewAlertCategory = UUID.fromString("00002a47-0000-1000-8000-00805f9b34fb");
public static UUID UUID_NewAlert = UUID.fromString("00002a46-0000-1000-8000-00805f9b34fb");
// descriptor used for enabling notify feature
public static UUID UUID_Descr_new = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
mGattServer = bluetoothManager.openGattServer(appContext, mGattServerCallback);
mGattServer.clearServices();
waitServiceAdded = true;
BluetoothGattService service = new BluetoothGattService(Vbe_Ans.UUID_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characSupportedNewAlerCategory =
new BluetoothGattCharacteristic(Vbe_Ans.UUID_SupportedNewAlertCategory,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic characNewAlert =
new BluetoothGattCharacteristic(Vbe_Ans.UUID_NewAlert,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ );
BluetoothGattDescriptor bgd = new BluetoothGattDescriptor(UUID_Descr_new,
BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
characNewAlert.addDescriptor(bgd);
...
ret = service.addCharacteristic(characSupportedNewAlerCategory);
ret = service.addCharacteristic(characNewAlert);
...
ret = mGattServer.addService(service);
while(waitServiceAdded);
waitServiceAdded = true;
//init second service similar way as previous
while(waitServiceAdded);
Then I clear waiting flag waitServiceAdded in GATT server call back. (I'm java newbie so you might want to use some thread mutex synchro to access boolean?)
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onServiceAdded (int status,
BluetoothGattService service){
waitServiceAdded = false;
}

Related

Bluetooth Low Energy notifications

I want to receive characteristic updates notifications from server to client. Both server and client are my android apps. There are a question and a problem.
I have found a "solution" that I need to add this code to subscribe, but add where? On the server side or on the client side?
val CCC_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb"
deviceGatt.setCharacteristicNotification(characteristic, true)
val desc = characteristic.getDescriptor(UUID.fromString(CCC_DESCRIPTOR_UUID))
deviceGatt.writeDescriptor(desc, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
The problem is that there are no descriptors at all. When server hosts a characteristic I check and there are 0 descriptors. There should be a CCCD by default but there is no one. The same happens when client discovers a service. There are no descriptors connected to characteristic. Can I add one by myself?
Edited:
I have tried to implement according to advice. Without manual adding CCCD on the server side there are no descriptors visible on the client side. So, on the Server side we have:
gattServer!!.addService(generateGattService())
...
private fun generateGattService(): BluetoothGattService {
val service = BluetoothGattService(GAME_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
characteristic = BluetoothGattCharacteristic(
MESSAGE_UUID,
BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
BluetoothGattCharacteristic.PERMISSION_WRITE
)
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val cccd = BluetoothGattDescriptor(
CCC_DESCRIPTOR_UUID,
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
)
characteristic!!.addDescriptor(cccd)
service.addCharacteristic(characteristic)
return service
}
On the Client side we have:
private fun onConnected(discoveredGatt: BluetoothGatt){
val service = discoveredGatt.getService(GAME_UUID)
val messageCharacteristic = service.getCharacteristic(MESSAGE_UUID)
discoveredGatt.setCharacteristicNotification(messageCharacteristic, true)
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val desc = messageCharacteristic.getDescriptor(CCC_DESCRIPTOR_UUID)
//desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
//discoveredGatt.writeDescriptor(desc)
Log.i(TAG_CLIENT, "Connected C -> S")
}
There are 2 commented rows. Using debugger I got that this way there is a descriptor connected to characteristic. I can send messages C -> S but this descritor has null value. If we uncomment those 2 rows, there is a descriptor value but messages don't come from anyone to anyone.
P.S. Code to write C -> S:
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
characteristic.value = msg
deviceGatt.writeCharacteristic(characteristic)
Code to write S -> C:
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
characteristic.value = msg
gattServer.notifyCharacteristicChanged(deviceToNotify, characteristic, false)
See below the answers to your two questions:-
The setCharacteristicNotification() method is used to enable or disable notifications for a specific characteristic on a GATT server device. Therefore, this method should be added on the GATT client side, because it is the client that subscribes to notifications for a particular characteristic.
As can be seen from this answer, you need the following to provide a notifiable characteristic on the GATT server side:-
a) Create a Bluetooth characteristic from your UUID:
public static final UUID NEW_SERVICE = UUID.fromString("0000ffff-0000-1000-8000-00805f9b34fb");
public static final UUID NEW_VALUE = UUID.fromString("0000fffe-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic characteristic = gatt.getService(NEW_SERVICE).getCharacteristic(NEW_VALUE);
b) Enable notifications
gatt.setCharacteristicNotification(characteristic,true);
c) Finally, set the CCCD to allow server initiated updates:-
public static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
Below are some useful links for both GATT client and GATT server side:-
Why does setCharacteristicNotification() not enable notifications
The ultimate guide to Android BLE development
Making Android BLE work
Finally I have found a solution. Posting here for the case if someone face the same.
So there are steps to enable notifications:
Server hosts:
val service = BluetoothGattService(GAME_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
characteristic = BluetoothGattCharacteristic(
MESSAGE_UUID,
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ or BluetoothGattCharacteristic.PERMISSION_WRITE)
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val cccd = BluetoothGattDescriptor(
CCC_DESCRIPTOR_UUID,
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE)
characteristic!!.addDescriptor(cccd)
service.addCharacteristic(characteristic)
Server GattCallbacck overrides onDescriptorReadRequest and onDescriptorWriteRequest with lines respectively
server.gattServer!!.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
and
server.gattServer!!.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null)
Client does:
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val desc = messageCharacteristic.getDescriptor(CCC_DESCRIPTOR_UUID)
discoveredGatt.setCharacteristicNotification(messageCharacteristic, true)
desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
discoveredGatt.writeDescriptor(desc)
GattCallback overrides DEPRECATED!!! version of onCharacteristicChanged:
override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?)
https://github.com/alexanderlavrushko/BLEProof-collection was very helpful but requires few scanning fixes to work.

Android BLE Gatt Server with multiple services - onCharacteristicWriteRequest() no char for handle

I am trying to build a BLE Gatt Server with multiple custom services and multiple characteristics.
To begin with I used the Google Example: https://github.com/androidthings/sample-bluetooth-le-gattserver/tree/master/kotlin
This was straight forward and worked very well. I modified the UUIDs to fit mine and I could receive notifications and write to the chars with no problem.
This is where I define the services and chars:
fun createTimeService(): BluetoothGattService {
val service = BluetoothGattService(TIME_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
// Current Time characteristic
val currentTime = BluetoothGattCharacteristic(CURRENT_TIME,
//Read-only characteristic, supports notifications
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ)
val configDescriptor = BluetoothGattDescriptor(CLIENT_CONFIG,
//Read/write descriptor
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE)
currentTime.addDescriptor(configDescriptor)
// Local Time Information characteristic
val localTime = BluetoothGattCharacteristic(LOCAL_TIME_INFO,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(currentTime)
service.addCharacteristic(localTime)
return service
}
fun createSerialService(): BluetoothGattService {
val service = BluetoothGattService(serialPortServiceID,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
val serialData = BluetoothGattCharacteristic(serialDataCharacteristicID,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(serialData)
return service
}
And here I am applying them to my server:
private fun startServer() {
bluetoothGattServer = bluetoothManager.openGattServer(this, gattServerCallback)
bluetoothGattServer?.addService(TimeProfile.createTimeService())
?: Log.w(TAG, "Unable to create GATT server")
bluetoothGattServer?.addService(TimeProfile.createSerialService())
?: Log.w(TAG, "Unable to create GATT server")
// Initialize the local UI
updateLocalUi(System.currentTimeMillis())
}
I would expect that everything would be working like before after adding the second service. But now if I try to write/subscribe to any of the characteristics (doesn't matter in which service) I just receive this:
W/BluetoothGattServer: onCharacteristicWriteRequest() no char for handle 42
W/BluetoothGattServer: onDescriptorWriteRequest() no desc for handle 43
I found what was going wrong. Apparently you cannot just add all services at once like I did. Adding the second service before the first one was confirmed lead to an Exception setting the services to null.
In the end I solved this by adding only one service initially.
Then in the onServiceAdded() Callback of the BleGattServerCallback() I started one after another.

Android Bluetooth Low Energy Getting response to specific request

There's something I'm not quite getting with using Gatt to communicate with a BLE device.
According to this:
https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback)
BluetoothGatt gatt = device.connectGatt(context,true,new BluetoothGattCallback(){....})
I can connect to a BLE device and give it a callbacks object to be notified on stuff like onCharacteristicRead and onCharacteristicWrite
What I'm not getting is how do which write corresponds to which read callback?
This method signatures are:
public void onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
public void onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
so if I do:
BluetoothGattCharacteristic char = gatt.getService(UART_SERVICE_UUID).getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
char1.setValue("command1");
gatt.writeCharacteristic(char);
char1.setValue("command2");
gatt.writeCharacteristic(char);
in the onCharacteristicRead callback, how do I know if the characteristic.getStringValue() is for command1 or command2?
Thanks!
There are a couple of important things to know about when using BluetoothGatt.writeCharacteristic() and related BluetoothGatt methods.
Please notice that writeCharacteristic() (and many other methods of BluetoothGatt) return a boolean. When the result is false, it means that the operation was not successfully initiated. When does that occur? See the next item.
Using aBluetoothGatt object, it is not possible to initiate two operations at the same time. The call to initiate the second will fail and return false. After calling writeCharacteristic(), the code must wait for the callback response (onCharacteristicWrite) before issuing another write. In the sample code for this question the second call to writeCharacteristic() will almost certainly return false for this reason.
In this way, if each BluetoothGatt operation waits for a callback on the previously issued command, you can successfully pair command initiations and callbacks -- really you are forced to.
In this implementation you should wait for the onCharacteristicWrite after the first write before issuing the next write operation. But this holds if you did not change the write type with setWriteType (int writeType) from the default WRITE_TYPE_DEFAULT, which calls back on write operations. You might need to keep state for these ops.
Another option is to have a command code (e.g. 1 byte) in the response so you can map it to the command.
private String heartRate;
private String temperature;
...
char.setValue("command1");
gatt.writeCharacteristic(char);
...
Then in onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) check if the heartRate variable is null. If it is, get the value of the characteristic and assign it to the heartRate variable. Then do characteristic.setValue("command2");
gatt.writeCharacteristic(characteristic); and it will call onCharacteristicWrite(...) again.
If the heartRate variable is not null, get the value and assign it to the temperature variable. You can also use the UUID of the passed characteristic to differentiate between what the value is for.
Bonus: A single characteristic can contain multiple descriptors. You can have two descriptors with their own UUID for that characteristic. You can then call characteristic.getDescriptor(UUIDDescriptor).getValue().
As I've already explained here, I'll give the answer I'd like to have when I've started "playing" with GATT BLE.
In order to create kind of an API (request with parameter(s) => response, as it's sais in the title of this question), follow these steps:
create a GATT service with:
a Write characteristic (unlike read characteristic, write one receives data) to manage request (with parameter(s) through the data payload)
a Read characteristic to manage reponse as notification
a Client Characteristic Configuration Descriptor for the Read characteristic to enable notification (standard 16-bit CCCD UUID: 0x2902).
Client write Client Characteristic Configuration Descriptor to enable notification/indication (once after ble connection)
Client write request Characteristic (request)
GATT server send response "status" OK for write Characteristic (request)
GATT server send indication (doesn't wait for acknowledge from client) with "data" from read Characteristic
Concretly, how to do that with FreeRTOS?
Create your GATT service as follow:
static const BTAttribute_t pxAttributeTable[] =
{
{
.xServiceUUID =
{
.uu.uu128 = GATT_SERVICE_UUID,
.ucType = eBTuuidType128
}
},
{
.xAttributeType = eBTDbCharacteristic,
.xCharacteristic =
{
.xUuid =
{
.uu.uu128 = GATT_CHARACTERISTIC_REQUEST_UUID,
.ucType = eBTuuidType128
},
.xPermissions = (IOT_BLE_CHAR_WRITE_PERM),
.xProperties = (eBTPropWrite)
}
},
{
.xAttributeType = eBTDbCharacteristic,
.xCharacteristic =
{
.xUuid =
{
.uu.uu128 = GATT_CHARACTERISTIC_RESPONSE_UUID,
.ucType = eBTuuidType128
},
.xPermissions = (IOT_BLE_CHAR_READ_PERM),
.xProperties = (eBTPropRead | eBTPropNotify)
}
},
{
.xAttributeType = eBTDbDescriptor,
.xCharacteristicDescr =
{
.xUuid =
{
.uu.uu16 = 0x2902,
.ucType = eBTuuidType16
},
.xPermissions = (IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM)
}
}
};

How can Android broadcast BLE local name like in iOS?

I've got an Android app advertising ble broadcast data with a service uuid and local name. The problem is that this "local name" (aka bluetooth device name) is limited to 8 characters, each is a 16-bit unicode representation, thus 2-bytes per character. Anytime I try to change the name of the device to 9 characters long, ble broadcasting fails to start due to error 1 which is
public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
I know the GAP profile broadcast packet is 27 bytes long and 7 are used for headers, thus 20 should remain free for use, not just 16?
Here's the real pickle that tickles my nickel:
When iOS is broadcasting ble advertisement header, I get the full local name of the device as part of the ScanRecord, not just limited to 16 bytes. Only part of the broadcast, I'm not establishing a GATT connection here.
How is iOS able to do this? For example, on my Android, I was able to retrieve a 14-character, 28 bytes long unique id from an iOS advertisement broadcast. 28 bytes is longer than the 27-byte limit imposed by bluetooth 4.0 standard. How is it that Android was able to pick up the full broadcast longer than 27 bytes? And how come my "local name" or device name can only be at most 8 characters or 16 bytes or it won't be able to start broadcasting?
This is my code:
final BluetoothLeAdvertiser advertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
//advertise settings
final AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
settingsBuilder.setConnectable(true);
settingsBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
//advertise data
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
ParcelUuid uuid = new ParcelUuid(UUID.fromString("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));
dataBuilder.addServiceUuid(uuid);
mBluetoothAdapter.setName("12345678"); //8 characters works, 9+ fails
dataBuilder.setIncludeDeviceName(true);
if (advertiser!=null) {
advertiser.startAdvertising(settingsBuilder.build(), dataBuilder.build(), mAdvertiseCallback);
}
mAdvertiseCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(LOGN, "BLE STARTED ADVERTISING BROADCAST "+settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(LOGN, "BLE ADVERTISING FAILED TO START: "+errorCode);
}
};
Is there a way for Android to include the full local name as part of the broadcast like iOS does?
Thanks!
If you view the overloads for StartAdvertising, you can see there is an additional parameter for the scan response. You can put the local name in the scan response data instead. Remember to remove setIncludeDeviceName from the data builder.
AdvertiseData.Builder scanResponseBuilder = new AdvertiseData.Builder();
mBluetoothAdapter.setName("123456789")
scanResponseBuilder.setIncludeDeviceName(true);
//...
if (advertiser!=null) {
advertiser.startAdvertising(settingsBuilder.build(), dataBuilder.build(), scanResponseBuilder.SetIncludeDeviceName.build(), mAdvertiseCallback);
}

Finding out Android Bluetooth LE GATT Profiles

I've implemented the Android LE bluetooth example that find a heart rate monitor and connects to it. However, this example has a class that defines the GATT profile like this:
private static HashMap<String, String> attributes = new HashMap();
public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
static {
// Sample Services.
attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service");
attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service");
// Sample Characteristics.
attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");
attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String");
}
public static String lookup(String uuid, String defaultName) {
String name = attributes.get(uuid);
return name == null ? defaultName : name;
}
Now, what I'm wanting to do is change it so that this program finds any device with bluetooth le but I have no idea how Google have gotten the information for the heart rate measurement of the client characteristic config.
The Bluetooth SIG maintains a list of "Assigned Numbers" that includes those UUIDs found in the sample app:
https://www.bluetooth.com/specifications/assigned-numbers/
Although UUIDs are 128 bits in length, the assigned numbers for Bluetooth LE are listed as 16 bit hex values because the lower 96 bits are consistent across a class of attributes.
For example, all BLE characteristic UUIDs are of the form:
0000XXXX-0000-1000-8000-00805f9b34fb
The assigned number for the Heart Rate Measurement characteristic UUID is listed as 0x2A37, which is how the developer of the sample code could arrive at:
00002a37-0000-1000-8000-00805f9b34fb
In addition to #Godfrey Duke's answer, here's a method I use to extract the significant bits of the UUID:
private static int getAssignedNumber(UUID uuid) {
// Keep only the significant bits of the UUID
return (int) ((uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >> 32);
}
Example of usage:
// See https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml
private static final int GATT_SERVICE_HEART_RATE = 0x180D;
(...)
for (BluetoothGattService service : services) {
if (getAssignedNumber(service.getUuid()) == GATT_SERVICE_HEART_RATE) {
// Found heart rate service
onHeartRateServiceFound(service);
found = true;
break;
}
}
I just wanted to add a few things :
0000-1000-8000-00805f9b34fb isn't the sole root for bluetooth UUIDs out there.
*-0002a5d5c51b is commonly seen as well (see https://uuid.pirate-server.com/search?q=0002a5d5c51b)
anything can be used, and has been used :
see https://uuid.pirate-server.com/search?q=gatt
see around https://github.com/edrosten/bluez/blob/master/monitor/uuid.c#L543 (includes uint16 & uint128 integers)
feel free to use whatever you want to use, but beware of using UUIDv1 UUIDs if you care significantly about security, because they embed a timestamp and a mac address which can be used to track you, to some extent, even if you use an online generator, see https://uuid.pirate-server.com/search?q=0800200c9a66 for all the examples of UUIDs using https://www.famkruithof.net/uuid/uuidgen?typeReq=-1 as a generator
Here's the page on the gatt callback: https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html
You need to use the BluetoothGatt.discoverServices();
and then in the callback onServicesDiscovered(...) i think you need to useBluetoothGatt.getServices();

Categories

Resources