Dino Tw
Dino Tw

Reputation: 3321

Confused by BluetoothGattServerCallback#onCharacteristicWriteRequest

Got confused by the Android BluetoothGattServerCallback#onCharacteristicWriteRequest method.

  1. For the parameter preparedWrite, I thought it would be my responsibility to implement the write operation, how does the callback know when I should queue the message?

  2. The documentation says An application must call BluetoothGattServer.sendResponse(BluetoothDevice, int, int, int, byte[]) to complete the request., what if responseNeeded is false?

  3. Calling BluetoothGattServer#sendResponse(BluetoothDevice, int, int, int, byte[]) seems always returns the value in the request, no matter what I set to value. Is it expected?

Upvotes: 1

Views: 1312

Answers (1)

BitByteDog
BitByteDog

Reputation: 3484

The preparedWrite is used to support long writes (greater than the MTU).

BluetoothGattServerCallback#onCharacteristicWriteRequest works in conjunction with BluetoothGattServerCallback#onExecuteWrite to reassemble fragmented data sent by the peer device. To answer your questions:

  1. The parameter preparedWriteis true when a fragment of the incoming data is received, i.e. the data needs to be queued until more arrives. BluetoothGattServerCallback#onExecuteWrite will be called after the final fragment has been received and the sent charactistic value can be fully assembled.

  2. If responseNeeded is false, don't call BluetoothGattServer.sendResponse(BluetoothDevice, int, int, int, byte[]).

  3. Just send an empty value.

    Here is an example of onCharacteristicWriteRequest:

                @Override
                public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
                    super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
                    Log.d("SVC", "BluetoothGattServerCallback.onCharacteristicWriteRequest with " + value.length + " bytes");
                    if(preparedWrite) {
                        handleInputFragment(device, characteristic.getUuid(), value);
                    } else {
                        handleInputMessage(device, characteristic.getUuid(), value);
                    }
                    if(responseNeeded) {
                        Log.d("SVC", "sending response to write request for characteristic: " + characteristic.getUuid());
                        if(!gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, new byte[0])) {
                            Log.e("SVC", "response to characteristic write request failed");
                        }
                    }
                }

and of onExecuteWrite

                @Override
                public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
                    Log.d("SVC", "BluetoothGattServerCallback.onExecuteWrite " + (execute ? "execute" : "cancelled"));
                    super.onExecuteWrite(device, requestId, execute);
                    Iterator<InputBuffer> itr = inputBuffers.iterator();
                    while(itr.hasNext()) {
                        InputBuffer buf = itr.next();
                        if(buf.device.equals(device)) {
                            itr.remove();
                            if(execute) {
                                ByteArrayOutputStream os = new ByteArrayOutputStream();
                                for (byte[] b : buf.bytes) {
                                    os.write(b, 0, b.length);
                                }
                                handleInputMessage(device, buf.characteristicUuid, os.toByteArray());
                            }
                        }
                    }
               }

The implementation of the InputBuffer mechanism is something like

    private class InputBuffer {
        final BluetoothDevice device;
        final UUID characteristicUuid;
        final List<byte[]> bytes;

        InputBuffer(BluetoothDevice device, UUID characteristicUuid, byte[] value) {
            this.device = device;
            this.characteristicUuid = characteristicUuid;
            this.bytes = new ArrayList<>();
            this.bytes.add(value);
        }
    }

    private List<InputBuffer> inputBuffers = new LinkedList<>();

    private void handleInputFragment(BluetoothDevice device, UUID characteristicUuid, byte[] value) {
        Log.d("SVC", "handling input Fragment");
        for(InputBuffer buf : inputBuffers) {
            if(buf.device.equals(device)) {
                buf.bytes.add(value);
                return;
            }
        }
        inputBuffers.add(new InputBuffer(device, characteristicUuid, value));
    }

The implementation of handleInputMessage is application specific.

Upvotes: 4

Related Questions