简体   繁体   中英

Android BLE: onDescriptorWrite, onCharacteristicWrite, onCharacteristicRead none are being called, but other callbacks work

I am writing an Android client that connects to a custom BLE server (peripheral) using a custom service and characteristics. I have verified that I can communicate with the device using the Android app BLE Scanner, and have also verified that I have correct UUIDs for the various characteristics. I have written code to write to the write-only characteristic, and the onCharacteristicWrite callback is never called. I have also written code to enable notifications on the read/notify characteristic, including updating the CCC descriptor to indicate notifications enabled, but neither the onDescriptorWrite, onCharacteristicWrite nor onCharacteristicChanged callback is called. I know that the BluetoothGattCallback is properly registered, because I do get calls to onConnectionStateChanged and onServicesDiscovered.

This code enables notifications on a given characteristic:

    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                          boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(CLASS_NAME, "BluetoothAdapter not initialized");
        return;
    }
    // Check if this characteristic actually has NOTIFY property
    if((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0 ) {
        Log.e(CLASS_NAME, "Characteristic does not support notifications");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

    // For characteristics that support it, write to the CCC descriptor
    // that notifications are enabled.
    if (enabled) {
        if (TX_PERIPHERAL_TO_CENTRAL.equals(characteristic.getUuid())) {
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                    UUID.fromString(GattAttributes.TX_PERIPHERAL_TO_CENTRAL_CCC));
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            if (!mBluetoothGatt.writeDescriptor(descriptor)) {
                Log.d(CLASS_NAME, "Write to descriptor failed: "+TX_PERIPHERAL_TO_CENTRAL_CCC);
            }
        }
    }
}

I can see in the logs that this gets called and that the writeDescriptor() call succeeds. However, onDescriptorWrite is never called, neither is onCharacteristicChanged.

Here is the code for the callbacks:

        @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic) {
        Log.d(CLASS_NAME, "in onCharacteristicChange() characteristic = "+characteristic.getUuid());
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorWrite(gatt, descriptor, status);
        Log.d(CLASS_NAME, "in onDescriptorWrite() status = "+status+", descriptor = "+descriptor.getUuid());
        Log.d(CLASS_NAME, "  descriptor value = "+descriptor.getValue());
    }

As you can see, I should be seeing something in the logs if either of these were called.

There's a similar issue with characteristic writes. Here is the code that performs a write, but onCharacteristicWrite is never called after this:

    public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic, String data) {
    boolean result = false;
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(CLASS_NAME, "BluetoothAdapter not initialized");
        return false;
    }
    // Check if this characteristic actually has WRITE property
    if((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 ) {
        Log.e(CLASS_NAME, "Characteristic is not writeable");
        return false;
    }
    byte[] ascii = data.getBytes(StandardCharsets.US_ASCII);
    if (characteristic.setValue(ascii)) {
        result = mBluetoothGatt.writeCharacteristic(characteristic);
    }
    return result;
}

In this case, writeCharacteristic() always returns false, despite the fact that I check to make sure it's a writable characteristic.

I have also used BLE Scanner to make sure that the characteristics I'm using can successfully be written to and read using notifications, so whatever the problem is, it's on my end. And apologies for the messy code - it's definitely in an incomplete state.

I've managed to figure this out on my own. The reason the attempts to write and turn on notifications were failing is the way in which I was managing the BluetoothGattCharacteristic objects. Using sample code I had found online, I was caching all of the BluetoothGattCharacteristic objects that are returned from BluetoothGatt.getServices() . I was then fetching each BluetoothGattCharacteristic from the cache and using it to make calls to operations such as BluetoothGatt.writeCharacteristic() or BluetoothGatt.setCharacteristicNotification() . Apparently, this is not the correct approach.

I have since modified my code so that I fetch a fresh copy of the service and characteristic object from BluetoothGatt before making any of the calls that were failing, using the method shown here:

/**
 * Fetches a characteristic from the GATT server.
 * @param serviceUUID unique id of the service that has the characteristic
 * @param characteristicUUID unique id of the characteristic
 * @return the requested characteristic, or null
 */
private BluetoothGattCharacteristic getCharacteristic(UUID serviceUUID, UUID characteristicUUID) {
    if (characteristicUUID == null) {
        Log.e(CLASS_NAME, "Attempt to fetch characteristic using null characteristic UUID");
        return null;
    }
    if (serviceUUID == null) {
        Log.e(CLASS_NAME, "Attempt to fetch characteristic "+characteristicUUID+" using null service UUID");
        return null;
    }
    BluetoothGattCharacteristic characteristic = null;
    BluetoothGattService service = mBluetoothGatt.getService(serviceUUID);
    if (service != null) {
        characteristic = service.getCharacteristic(characteristicUUID);
        if (characteristic == null) {
            Log.d(CLASS_NAME, "getCharacteristic(): Unable to obtain characteristic.");
        }
    }
    else {
        Log.d(CLASS_NAME, "getCharacteristic(): Unable to obtain service.");
    }

    return characteristic;
}

With this, I now get the expected callbacks to onCharacteristicWrite , onCharacteristicChange , onDescriptorWrite , etc. I hope this is helpful to someone in the future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM