Reputation: 3321
Got confused by the Android BluetoothGattServerCallback#onCharacteristicWriteRequest method.
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?
The documentation says An application must call BluetoothGattServer.sendResponse(BluetoothDevice, int, int, int, byte[]) to complete the request.
, what if responseNeeded
is false?
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
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:
The parameter preparedWrite
is 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.
If responseNeeded
is false, don't call BluetoothGattServer.sendResponse(BluetoothDevice, int, int, int, byte[])
.
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