Orbit Orbit - 2 months ago 30
Android Question

Reading from a BluetoothGattCharacteristic is failing

Im trying to read the value stored in a

BluetoothGattCharacteristic
. The following is my
BluetoothGattCallback
code, where most of the action takes place:

private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
Log.i(TAG, "Getting services....");
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
}
}

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService serv = gatt.getService(Constants.MY_UUID);
if (serv != null) {
BluetoothGattCharacteristic characteristic = serv.getCharacteristic(Constants.ANOTHER_UUID);
boolean res = gatt.readCharacteristic(characteristic);
if (res) {
Log.d(TAG, "res was true");
} else {
Log.d(TAG, "res was false");
}
}
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}

@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "Succesfully read characteristic: " + characteristic.getValue().toString());
} else {
Log.d(TAG, "Characteristic read not successful");
}
}
};


So to read from the characteristic, i'm attempting to use the
gatt.readCharacteristic()
method, which takes a characteristic and returns a boolean indicating a successful operation or not. Here, this method is returning
false
(printing "res was false"), indicating it failed.

There is no error message being printed. What is the proper way to read a characteristic? Why would this method be returning
false
?

EDIT:
As suggested by Inferno, went ahead and downloaded the needed sources and then set a breakpoint in the
BluetoothGatt
readCharacteristic()
method:

Here is the
readCharacteristic()
method in android-23..\BluetoothGatt

public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
if ((characteristic.getProperties() &
BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;


(characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)
is returning 0 so
false
is being immediately returned. Now according to the debugger
characteristic.getProperties()
is returning a value of
8
, while
BluetoothGattCharacteristic.PROPERTY_READ
has a static int value of
0x02
.

As I understand,
0x08 & 0x02
== 0. Since the
PROPERTY_READ
is a hardcoded value, I assume something is wrong with the value returned from
characteristic.getProperties()
. What could be going wrong here?

Answer

What is the proper way to read a characteristic?

First of all, you call gatt.readCharacteristic(characteristic) from inside of the onServicesDiscovered() callback, which is alright. I can't see any serious flaws in your code.

What you could add in onConnectionStateChange() is an additional check before you verify newState == BluetoothProfile.STATE_CONNECTED:

if (status == BluetoothGatt.GATT_SUCCESS) { ...

Why would this method be returning false?

I checked the android source of BluetoothGatt here and it turns out, the return value of false is returned in many different cases as you can see in the code below:

public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if ((characteristic.getProperties() &
            BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;

    if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
    if (mService == null || mClientIf == 0) return false;

    BluetoothGattService service = characteristic.getService();
    if (service == null) return false;

    BluetoothDevice device = service.getDevice();
    if (device == null) return false;

    synchronized(mDeviceBusy) {
        if (mDeviceBusy) return false;
        mDeviceBusy = true;
    }

    try {
        mService.readCharacteristic(mClientIf, device.getAddress(),
            characteristic.getInstanceId(), AUTHENTICATION_NONE);
    } catch (RemoteException e) {
        Log.e(TAG,"",e);
        mDeviceBusy = false;
        return false;
    }

    return true;
}

So what I recommend you to do is, start the debugger in Android Studio and set a breakpoint inside the readCharacteristic() method (in BluetoothGatt.java) and carefully step through the code to see where false gets returned. That way you will hopefully be able to localize the issue. Besides that, anything else would be wild guessing.

Of course you need to have the sources downloaded to be able to view BluetoothGatt.java. But Android Studio will give you a small yellow bar at the top of the editor which asks you if you want to download and install. Just do it and restart Android Studio after the download is complete. Then you should be able to set a breakpoint in BluetoothGatt.java.

UPDATE:

As I understand, 0x08 & 0x02 == 0. Since the PROPERTY_READ is a hardcoded value, I assume something is wrong with the value returned from characteristic.getProperties(). What could be going wrong here?

According to BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 533, the value of 0x8 which is returned by characteristic.getProperties() means, that your characteristic has write only permissions. Not a surprise that all reading attempts fail. In other words: your bluetooth device does not allow you to read that particular characteristic.

Quote from the specification:

The Characteristic Properties bit field determines how the Characteristic Value can be used, or how the characteristic descriptors (see Section 3.3.3) can be accessed.

Comments