Attempting to read characteristics from a GATT service, the list is incomplete compared to the list I get from an iOS device, or the manufacturer guide.
I am reading the characteristics as follows:
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnectionState = STATE_CONNECTED;
mBluetoothGatt = gatt;
if (shouldStartWriting && writeCharacteristicsQueue.peek() != null) {
mBluetoothGatt.writeCharacteristic(writeCharacteristicsQueue.poll());
} else {
EventBus.getDefault().post(new BTGattStatusEvent(BTGattStatusEvent.Status.CONNECTED));
Log.i(TAG, "Attempting to start service discovery:" +
gatt.discoverServices());
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mConnectionState = STATE_DISCONNECTED;
mBluetoothGatt = null;
EventBus.getDefault().post(new BTGattDisconnectedEvent());
}
}
#Override
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
EventBus.getDefault().post(new BTGattStatusEvent(BTGattStatusEvent.Status.DISCOVERED));
if (status == BluetoothGatt.GATT_SUCCESS) {
for (BluetoothGattService service : gatt.getServices()) {
LOGD(TAG, "Found Service " + service.getUuid().toString());
discoverCharacteristics(service);
}
gatt.readCharacteristic(readCharacteristicsQueue.poll());
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
// Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
parseCharacteristic(characteristic);
LOGD(TAG, String.format("%s | %s ", characteristic.getUuid(), characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)));//(BluetoothGattCharacteristic.FORMAT_UINT16));
if (readCharacteristicsQueue.peek() != null) {
gatt.readCharacteristic(readCharacteristicsQueue.poll());
} else {
EventBus.getDefault().post(new BTGattStatusEvent(BTGattStatusEvent.Status.DONE_DISCOVERING));
}
}
}
and
private void discoverCharacteristics(BluetoothGattService service) {
for (BluetoothGattCharacteristic gattCharacteristic : service.getCharacteristics()) {
LOGD(TAG, "Discovered UUID: " + gattCharacteristic.getUuid());
readCharacteristicsQueue.add(gattCharacteristic);
}
}
I also attempted to get this characteristic using getCharacteristic, but I received a null.
The characteristics found are:
Discovered UUID: 0000fff1-0000-1000-8000-00805f9b34fb
Discovered UUID: 0000fff2-0000-1000-8000-00805f9b34fb
Discovered UUID: 0000fff3-0000-1000-8000-00805f9b34fb
Discovered UUID: 0000fff4-0000-1000-8000-00805f9b34fb
Discovered UUID: 0000fff5-0000-1000-8000-00805f9b34fb
However, on iOS and according to the manufacturer, there should also be a 0000fff7.
Why isn't this characteristic being read?
UPDATE:
Nothing happened here. Not one of Android's BLE scanning apps can discover it.
Related
I am trying to connect to the i-phone via Android Bluetooth + BLE.
My goal is to read iOS notifications via Android Bluetooth + BLE.
I am able to show the i-phone Bluetooth in the android app and was able to connect to the i-phone but I am unable to found the Notification characteristic.
I got notification characteristic UUID from this link I am using Notification Source: UUID
Here is my BluetoothGattCallback:
public BluetoothGattCallback mCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
final BluetoothGatt mGatt = gatt;
Handler handler;
handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
mGatt.discoverServices();
}
});
//gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
try
{
Log.i("no_conn", "Connection unsuccessful with status"+status);
//mGatt.disconnect();
mGatt.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.i("Not success", "Device service discovery unsuccessful, status " + status);
return;
}
List<BluetoothGattService> matchingServices = gatt.getServices();
gatt.getService(UUID.fromString(SERVICE_STRING));
List<BluetoothGattCharacteristic> matchingCharacteristics = BluetoothUtils.findCharacteristics(gatt);
if (matchingCharacteristics.isEmpty()) {
Log.i("No characteristics", "Unable to find characteristics.");
showToast("No characteristic found");
return;
}else {
showToast("characteristic found");
}
}
};
Here is my findCharacteristics function:
public static List<BluetoothGattCharacteristic> findCharacteristics1(BluetoothGatt bluetoothGatt) {
List<BluetoothGattCharacteristic> matchingCharacteristics = new ArrayList<>();
List<BluetoothGattService> serviceList = bluetoothGatt.getServices();
BluetoothGattService service = null;
for (BluetoothGattService bgservice : serviceList) {
String serviceIdString = bgservice.getUuid()
.toString();
if (matchesServiceUuidString(serviceIdString)) {
service = bgservice;
}
}
if (service == null) {
Log.i("Null service", "Null service.");
return matchingCharacteristics;
}
return matchingCharacteristics;
}
Here I am matching with serviceIdString with the Notification Source: UUID.
Am I missing anything?
I am trying to get data from a glucose meter and I am not able to find good resources regarding the implementation on internet. Here is what I have been able to implement till now:
I am scanning the devices using BluetoothAdapter.LeScanCallback Interface:
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
if ((device.getName() != null) && !bleDevices.contains(device)) {
bleDevices.add(device);
}
}
After getting the devices I am connecting to device using:
device.connectGatt(MainActivity.this, true, bleGattCallBack);
In BluetoothGattCallback class I am able to get the status connected in:
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
connectionState = STATE_CONNECTED;
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
connectionState = STATE_DISCONNECTED;
gatt.close();
}
}
After that onServicesDiscovered gets called
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> gattServices = gatt.getServices();
for (BluetoothGattService gattService : gattServices) {
String serviceUUID = gattService.getUuid().toString();
if (serviceUUID.equals("00001808-0000-1000-8000-00805f9b34fb")) {
List<BluetoothGattCharacteristic> characteristics = gattService.getCharacteristics();
for (BluetoothGattCharacteristic characteristic : characteristics) {
if (characteristic.getUuid().equals(UUID.fromString("00002a18-0000-1000-8000-00805f9b34fb"))) {
BluetoothGattCharacteristic charGM =
gatt.getService(UUID.fromString("00001808-0000-1000-8000-00805f9b34fb"))
.getCharacteristic(UUID.fromString("00002a18-0000-1000-8000-00805f9b34fb"));
gatt.setCharacteristicNotification(charGM, true);
glucoseCharacteristic = characteristic;
BluetoothGattDescriptor descGM = charGM.getDescriptor(UUID.fromString(BleUuid.CHAR_CLIENT_CHARACTERISTIC_CONFIG_STRING));
descGM.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descGM);
} else if (characteristic.getUuid().equals(UUID.fromString("00002a52-0000-1000-8000-00805f9b34fb"))) {
BluetoothGattCharacteristic charRACP =
gatt.getService(UUID.fromString("00001808-0000-1000-8000-00805f9b34fb"))
.getCharacteristic(UUID.fromString("00002a52-0000-1000-8000-00805f9b34fb"));
gatt.setCharacteristicNotification(charRACP, true);
BluetoothGattDescriptor descRACP = charRACP.getDescriptor(UUID.fromString(BleUuid.CHAR_CLIENT_CHARACTERISTIC_CONFIG_STRING));
descRACP.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descRACP);
}
}
}
} else {
}
}
After this onDescriptorWrite gets called:
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onDescriptorWrite: GATT_SUCCESS");
BluetoothGattCharacteristic writeRACPchar =
gatt.getService(UUID.fromString("00001808-0000-1000-8000-00805f9b34fb"))
.getCharacteristic(UUID.fromString("00002a52-0000-1000-8000-00805f9b34fb"));
byte[] data = new byte[2];
data[0] = 0x01; // Report Stored records
data[1] = 0x01; // All records
writeRACPchar.setValue(data);
gatt.writeCharacteristic(writeRACPchar);
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
Log.d(TAG, "onDescriptorWrite: GATT_INSUFFICIENT_AUTHENTICATION");
}
} else {
Log.d(TAG, "onDescriptorWrite: GATT_INSUFFICIENT_AUTHENTICATION");
}
}
and then onCharacteristicWrite
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for (byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
Log.d(TAG, "onCharacteristicWrite: " + status + "\n" + characteristic.getUuid() + "\n" + stringBuilder.toString() + "\n" + characteristic.getValue().length);
}
}
Now, how do I proceed further? I know that I will get the data in onCharacteristicRead but that is never called.
android 10 for BLE Bluetooth connection is not connected , i will set all permission also but i get connection status 133 issues ,how to solve this issues ,here i declare the scanning and callback code ,please check and give your idea
but below android 10 version is working fine,only issues on android 10 device ,give an idea to solve the issues
private void scanLeDevice(final boolean enable) {
if (scanner == null) {
scanner = btAdapter.getBluetoothLeScanner();
}
if (scanCallback == null) setScanCallback(null);
scanner.startScan(createScanFilters(),createScanSettings(),scanCallback);
isScanning = true;
}
ScanCallback scanCallback = new ScanCallback() {
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onScanResult(int callbackType, #NonNull ScanResult scanResult) {
if (connectedDevices.containsKey(scanResult.getDevice().getAddress())) {
return;
}
if (connectingDevices.contains(scanResult.getDevice().getAddress())) {
// If we're already connected, forget it
//Timber.d("Denied connection. Already connecting to " + scanResult.getDevice().getAddress());
return;
}
if (connectionGovernor != null && !connectionGovernor.shouldConnectToAddress(scanResult.getDevice().getAddress())) {
// If the BLEConnectionGovernor says we should not bother connecting to this peer, don't
return;
}
final BluetoothDevice device = btAdapter.getRemoteDevice(scanResult.getDevice().getAddress());
if (device == null) {
Log.e(TAG, "Device not found. Unable to connect.");
}
connectingDevices.add(scanResult.getDevice().getAddress());
// connectToDevice(device);
Timber.d("Initiating connection to " + scanResult.getDevice().getAddress());
device.connectGatt(context, false, mGattCallback, BluetoothDevice.TRANSPORT_LE );
}
#Override
public void onScanFailed(int i) {
Timber.e("Scan failed with code " + i);
}
};
#NonNull
BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(#NonNull BluetoothGatt gatt, int status, int newState) {
synchronized (connectedDevices) {
// It appears that certain events (like disconnection) won't have a GATT_SUCCESS status
// even when they proceed as expected, at least with the Motorola bluetooth stack
if (status != BluetoothGatt.GATT_SUCCESS)
Timber.w("onConnectionStateChange with newState %d and non-success status %s", newState, gatt.getDevice().getAddress());
Set<BluetoothGattCharacteristic> characteristicSet;
switch (newState) {
case BluetoothProfile.STATE_DISCONNECTING:
Timber.d("Disconnecting from " + gatt.getDevice().getAddress());
characteristicSet = discoveredCharacteristics.get(gatt.getDevice().getAddress());
for (BluetoothGattCharacteristic characteristic : characteristicSet) {
if (notifyUUIDs.contains(characteristic.getUuid())) {
Timber.d("Attempting to unsubscribe on disconneting");
setIndictaionSubscription(gatt, characteristic, false);
}
}
discoveredCharacteristics.remove(gatt.getDevice().getAddress());
break;
case BluetoothProfile.STATE_DISCONNECTED:
Timber.d("Disconnected from " + gatt.getDevice().getAddress());
connectedDevices.remove(gatt.getDevice().getAddress());
connectingDevices.remove(gatt.getDevice().getAddress());
if (transportCallback != null)
transportCallback.identifierUpdated(BLETransportCallback.DeviceType.CENTRAL,
gatt.getDevice().getAddress(),
Transport.ConnectionStatus.DISCONNECTED,
null);
characteristicSet = discoveredCharacteristics.get(gatt.getDevice().getAddress());
if (characteristicSet != null) { // Have we handled unsubscription on DISCONNECTING?
for (BluetoothGattCharacteristic characteristic : characteristicSet) {
if (notifyUUIDs.contains(characteristic.getUuid())) {
Timber.d("Attempting to unsubscribe before disconnet");
setIndictaionSubscription(gatt, characteristic, false);
}
}
} else
if ( status != BluetoothGatt.GATT_SUCCESS ) {
if ( status == 133) {
refreshDeviceCache(gatt);
}
}
gatt.close(); discoveredCharacteristics.remove(gatt.getDevice().getAddress());
break;
case BluetoothProfile.STATE_CONNECTED:
boolean mtuSuccess = gatt.requestMtu(BLETransport.DEFAULT_MTU_BYTES);
Timber.d("Connected to %s. Requested MTU success %b", gatt.getDevice().getAddress(),
mtuSuccess);
break;
}
super.onConnectionStateChange(gatt, status, newState);
}
}
#Override
public void onMtuChanged(#NonNull BluetoothGatt gatt, int mtu, int status) {
Timber.d("Got MTU (%d bytes) for device %s. Was changed successfully: %b",
mtu,
gatt.getDevice().getAddress(),
status == BluetoothGatt.GATT_SUCCESS);
mtus.put(gatt.getDevice().getAddress(), mtu);
// TODO: Can we craft characteristics and avoid discovery step?
boolean discovering = gatt.discoverServices();
Timber.d("Discovering services : " + discovering);
}
#Override
public void onServicesDiscovered(#NonNull BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS)
Timber.d("Discovered services");
else
Timber.d("Discovered services appears unsuccessful with code " + status);
// TODO: Keep this here to examine characteristics
// eventually we should get rid of the discoverServices step
boolean foundService = false;
try {
List<BluetoothGattService> serviceList = gatt.getServices();
for (BluetoothGattService service : serviceList) {
if (service.getUuid().equals(serviceUUID)) {
Timber.d("Discovered Service");
foundService = true;
HashSet<BluetoothGattCharacteristic> characteristicSet = new HashSet<>();
characteristicSet.addAll(service.getCharacteristics());
discoveredCharacteristics.put(gatt.getDevice().getAddress(), characteristicSet);
for (BluetoothGattCharacteristic characteristic : characteristicSet) {
if (notifyUUIDs.contains(characteristic.getUuid())) {
setIndictaionSubscription(gatt, characteristic, true);
}
}
}
}
if (foundService) {
synchronized (connectedDevices) {
connectedDevices.put(gatt.getDevice().getAddress(), gatt);
}
connectingDevices.remove(gatt.getDevice().getAddress());
}
} catch (Exception e) {
Timber.d("Exception analyzing discovered services " + e.getLocalizedMessage());
e.printStackTrace();
}
if (!foundService)
Timber.d("Could not discover chat service!");
super.onServicesDiscovered(gatt, status);
}
/**
* Subscribe or Unsubscribe to/from indication of a peripheral's characteristic.
*
* After calling this method you must await the result via
* {#link #onDescriptorWrite(BluetoothGatt, BluetoothGattDescriptor, int)}
* before performing any other peripheral actions.
*/
private void setIndictaionSubscription(#NonNull BluetoothGatt peripheral,
#NonNull BluetoothGattCharacteristic characteristic,
boolean enable) {
boolean success = peripheral.setCharacteristicNotification(characteristic, enable);
Timber.d("Request notification %s %s with sucess %b", enable ? "set" : "unset", characteristic.getUuid().toString(), success);
BluetoothGattDescriptor desc = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
desc.setValue(enable ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
boolean desSuccess = peripheral.writeDescriptor(desc);
Timber.d("Wrote descriptor with success %b", desSuccess);
}
#Override
public void onDescriptorWrite(#NonNull BluetoothGatt gatt, #NonNull BluetoothGattDescriptor descriptor,
int status) {
Timber.d("onDescriptorWrite");
if (status == BluetoothGatt.GATT_SUCCESS && transportCallback != null) {
if (Arrays.equals(descriptor.getValue(), BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
transportCallback.identifierUpdated(BLETransportCallback.DeviceType.CENTRAL,
gatt.getDevice().getAddress(),
Transport.ConnectionStatus.CONNECTED,
null);
} else if (Arrays.equals(descriptor.getValue(), BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
Timber.d("disabled indications successfully. Closing gatt");
gatt.close();
}
}
}
#Override
public void onCharacteristicChanged(#NonNull BluetoothGatt gatt, #NonNull BluetoothGattCharacteristic characteristic) {
Timber.d("onCharacteristicChanged %s with %d bytes", characteristic.getUuid().toString().substring(0,5),
characteristic.getValue().length);
if (transportCallback != null)
transportCallback.dataReceivedFromIdentifier(BLETransportCallback.DeviceType.CENTRAL,
characteristic.getValue(),
gatt.getDevice().getAddress());
super.onCharacteristicChanged(gatt, characteristic);
}
#Override
public void onCharacteristicWrite(#NonNull BluetoothGatt gatt,
#NonNull BluetoothGattCharacteristic characteristic, int status) {
Timber.d("onCharacteristicWrite with %d bytes", characteristic.getValue().length);
Exception exception = null;
if (status != BluetoothGatt.GATT_SUCCESS) {
String msg = "Write was not successful with code " + status;
Timber.w(msg);
exception = new UnknownServiceException(msg);
}
if (transportCallback != null)
transportCallback.dataSentToIdentifier(BLETransportCallback.DeviceType.CENTRAL,
characteristic.getValue(),
gatt.getDevice().getAddress(),
exception);
}
#Override
public void onReadRemoteRssi(#NonNull BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
}
};
now the app is working for change the bluetooth device support type
device.connectGatt(context, false, mGattCallback, BluetoothDevice.DEVICE_TYPE_LE);
but now the app is not working on huawei device , if any have idea for the device huawei for this issue
I was trying to read Heart Rate values from my Xiaomi Band 2. For this I tried using and adapting BluetoothLeGatt project from https://github.com/android/connectivity-samples/tree/master/BluetoothLeGatt .
So far I was able to survey the BLE devices nearby. After that I selected Xiaomi Band 2 and I could successfully list out all the services this device had, including Heart Rate Service. Inside Heart Rate Service I found the characteristic Heart Rate Measurement that I was looking for.
I tried to print data from this characteristic without success. I was unable to see any data in my log, even though I made some slight changes.
This is my LEScanCallback (with the slight changes done)
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
#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);
}
BluetoothGattCharacteristic characteristic =
gatt.getService(HEART_RATE_SERVICE_UUID)
.getCharacteristic(HEART_RATE_MEASUREMENT_CHAR_UUID);
System.out.println("HEART_RATE_MEASUREMENT_CHAR_UUID: "+HEART_RATE_MEASUREMENT_CHAR_UUID);
/*gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor); */
setCharacteristicNotification(characteristic,true);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
System.out.println("onCharacteristicRead: " + Arrays.toString(characteristic.getValue()));
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status){
super.onDescriptorWrite(gatt, descriptor, status);
System.out.println("onDescriptorWrite");
BluetoothGattCharacteristic characteristic = gatt.getService(HEART_RATE_SERVICE_UUID)
.getCharacteristic(HEART_RATE_MEASUREMENT_CHAR_UUID);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
characteristic.setValue(new byte[]{1, 1});
//gatt.writeCharacteristic(characteristic);
boolean success = gatt.readCharacteristic(characteristic);
System.out.println("success? "+success);
System.out.println("status: "+status);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
System.out.println("onCharacteristicChanged: " + Arrays.toString(characteristic.getValue()));
}
};
I don't get to the point onCharacteristicRead() or onCharacteristicChanged() is called. I believe it's because gatt.readCharacteristic(characteristic) is FALSE and status is 3 (GATT_WRITE_NOT_PERMITTED) in onDescriptorWrite() function.
Can somebody help me figuring out the problem? I'm starting to lose all hope...
I pair with my device, I connect to it, using this code
//this will try to connect to our bluetooth device
public void connectGatt(Context context, BluetoothDevice btDevice) {
this.btDevice = btDevice;
mBluetoothGatt = btDevice.connectGatt(context, false, mGattCallback);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mBluetoothGatt != null) {
mBluetoothGatt.requestMtu(512);
}
}
This is my GATT Callback:
private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
tryToConnect = false;
Log.i("BluetoothService", "BluetoothService onConnectionStateChange CONNECTED");
if (back != null)
back.onResponse(REMOVE_CALLBACKS);
Log.i("", "Connected to GATT server.");
boolean discover = mBluetoothGatt.discoverServices();
Log.i("", "Attempting to start service discovery:" + discover);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("BluetoothService", "BluetoothService onConnectionStateChange STATE_DISCONNECTED");
if (tryToConnect) {
if (back != null)
back.onResponse(REMOVE_CALLBACKS);
stopMonitoringBeacons();
stopListeningForBeacons();
mBluetoothAdapter.disable();
setSleep(1500);
mBluetoothAdapter.enable();
setSleep(1500);
Log.i("BluetoothService", "BluetoothService onConnectionStateChange WILL TRY CONNECT");
if (back != null)
back.onResponse(CONNECT);
tryToConnect = false;
}
}
}
#Override
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
tryToConnect = false;
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i("BluetoothService", "BluetoothService onConnectionStateChange onServicesDiscovered GATT_SUCCESS" + status);
Log.w("", "onServicesDiscovered GATT_SUCCESS: " + status);
List<BluetoothGattService> listBGS = mBluetoothGatt.getServices();
Log.i("", "list size: " + listBGS.size());
if (listBGS.size() > 0) {
if (back != null)
back.onResponse(CONFIGURE);
String character = "FF05";
if (justName)
character = "FF01";
setCharacteristic(gatt, character);
} else {
Log.i("BluetoothService", "BluetoothService onConnectionStateChange onServicesDiscovered GATT_SUCCESS but ZERO SERVICES: " + btDevice.getBondState());
if (btDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
setSleep(500);
if (btDevice.getBondState() == BluetoothDevice.BOND_BONDED)
mBluetoothGatt.discoverServices();
} else if (btDevice.getBondState() == BluetoothDevice.BOND_NONE) {
askPairDevice();
}
}
} else {
askPairDevice();
Log.w("BluetoothService", "BluetoothService onServicesDiscovered received: " + status);
}
}
#Override
// Result of a characteristic read operation
public void onCharacteristicRead(final BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic,
int status) {
handler.removeCallbacksAndMessages(null);
Log.i("BluetoothService", "BluetoothService onCharacteristicRead");
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i("BluetoothService", "BluetoothService onCharacteristicRead GATT_SUCCESS");
Log.w("", "BGC onCharacteristicRead GATT_SUCCESS: " + status + " / char: " + characteristic);
if (characteristic.getUuid().toString().toUpperCase().contains("FF05")) {
final String value = toHexadecimal(characteristic.getValue());
if (newBeacon == null || newBeacon.getName() == null) {
checkIfNeedsToChangeUUID(gatt, value);
} else if (newBeacon != null && (newBeacon.getUuid() != null || justName)) {
Log.i("BluetoothService", "BluetoothService new Beacon UUID is: " + value);
newBeacon.setUuid(Identifier.parse(value).toString());
if (back != null)
back.onResponse(CHANGED);
} else {
changeUUID(gatt, characteristic, value);
}
} else if (characteristic.getUuid().toString().toUpperCase().contains("FF01")) {
changeName(gatt, characteristic);
}
} else {
Log.i("", "");
}
}
};
The part that I am interested in is here:
public void changeUUID(final BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, String value) {
int size = value.length();
RandomString gen = new RandomString(size, new SecureRandom());
String uuid = gen.nextString();
Log.i("", "BluetoothService BGC value NEW UUID: " + uuid);
boolean change = characteristic.setValue(hexStringToByteArray(uuid));
Log.i("", "BluetoothService BGC value after: " + toHexadecimal(characteristic.getValue()) + " / has changed: " + change);
boolean statusWrite = gatt.writeCharacteristic(characteristic);
Log.i("", "BluetoothService BGC value after statusWRITE: " + statusWrite);
if (statusWrite) {
newBeacon.setUuid(Identifier.parse(toHexadecimal(characteristic.getValue())).toString());
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
setCharacteristic(gatt, "FF05");
}
});
} else {
if (back != null)
back.onResponse(ERROR);
}
}
Which will call this:
try {
Log.i("BluetoothService", "BluetoothService set characteristic: " + characteristic);
BluetoothGattCharacteristic btChar = null;
for (BluetoothGattService bgs : gatt.getServices()) {
for (final BluetoothGattCharacteristic bgc : bgs.getCharacteristics()) {
if (bgc.getUuid().toString().toUpperCase().contains(characteristic)) {
btChar = bgc;
gatt.readCharacteristic(bgc);
break;
}
}
}
final BluetoothGattCharacteristic btF = btChar;
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (btF != null)
gatt.readCharacteristic(btF);
}
}, 1000);
} catch (Exception e) {
Log.i("BluetoothService", "BluetoothService set characteristic ERROR: " + e.getMessage());
setCharacteristic(gatt, characteristic);
}
}
This works, but not 100%, I could say that it works on less than 50% of the cases. and I don't understand why. Can someone help?
I mean boolean statusWrite = gatt.writeCharacteristic(characteristic); Always returns to me "TRUE" so then if in the "changeUUID function I make after that another call, to try to log it, why is it NOT changed?
Also with other apps, if I check, my UUID is not changed, which is really weird. Why get the TRUE for the write, if value is the same?
If you look at the documentation for writeCharacteristic, you'll see this:
Returns boolean true, if the write operation was initiated successfully
The emphasis above is mine. Just because you successfully initiated the write doesn't mean it compleleted successfully, and indeed, in my apps I often see that it does not.
What you need to do is implement the callback public void onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status), then check the status to see if it is BluetoothGatt.GATT_SUCCESS. If not, you will need to retry the write. I usually make code that retries up to 10 times, then gives up after that, triggering code that presents an error to the user.
Most other Android Bluetooth APIs work this way -- discovering services, discovering characteristics, etc. Each one of these async operations can fail, and you have to implement the callbacks to find out if they actually succeeded or not, and add a retry strategy.