37 private static final String TAG =
"QtBluetoothGattServer";
40 @SuppressWarnings({
"CanBeFinal",
"WeakerAccess"})
42 @SuppressWarnings(
"WeakerAccess")
44 private Context qtContext =
null;
47 private BluetoothAdapter mBluetoothAdapter =
null;
48 private BluetoothManager mBluetoothManager =
null;
49 private BluetoothGattServer mGattServer =
null;
50 private BluetoothLeAdvertiser mLeAdvertiser =
null;
52 private ArrayList<BluetoothGattService> mPendingServiceAdditions =
53 new ArrayList<BluetoothGattService>();
55 private String mRemoteName =
"";
61 private String mRemoteAddress =
"";
64 return mRemoteAddress;
68 private static final int DEFAULT_LE_ATT_MTU = 23;
70 private int mSupportedMtu = DEFAULT_LE_ATT_MTU;
72 private static final int MAX_PENDING_WRITE_COUNT = 1024;
74 private static final int GATT_ERROR_PREPARE_QUEUE_FULL = 0x9;
76 private static final int BTLE_MAX_ATTRIBUTE_VALUE_SIZE = 512;
80 private class WriteEntry {
81 WriteEntry(BluetoothDevice remoteDevice,
Object target) {
82 this.remoteDevice = remoteDevice;
84 this.writes =
new ArrayList<Pair<byte[], Integer>>();
90 public final BluetoothDevice remoteDevice;
92 public final List<Pair<byte[], Integer>> writes;
94 private final List<WriteEntry> mPendingPreparedWrites =
new ArrayList<>();
100 mPendingPreparedWrites.clear();
101 ListIterator<WriteEntry> iterator = mPendingPreparedWrites.listIterator();
102 while (iterator.hasNext()) {
103 if (iterator.next().remoteDevice.equals(
device))
113 WriteEntry
entry =
null;
114 int currentWriteCount = 0;
119 for (WriteEntry e : mPendingPreparedWrites) {
122 currentWriteCount += e.writes.size();
126 if (currentWriteCount > MAX_PENDING_WRITE_COUNT) {
127 Log.w(TAG,
"Prepared write queue is full, returning an error.");
128 return GATT_ERROR_PREPARE_QUEUE_FULL;
138 return BluetoothGatt.GATT_SUCCESS;
146 private class ClientCharacteristicManager {
147 private final HashMap<BluetoothGattCharacteristic, List<Entry>> notificationStore =
new HashMap<BluetoothGattCharacteristic, List<Entry>>();
149 private class Entry {
150 BluetoothDevice
device =
null;
152 boolean isConnected =
false;
155 public void insertOrUpdate(BluetoothGattCharacteristic characteristic,
156 BluetoothDevice
device,
byte[] newValue)
158 if (notificationStore.containsKey(characteristic)) {
160 List<Entry> entries = notificationStore.get(characteristic);
161 for (
int i = 0;
i < entries.size();
i++) {
162 if (entries.get(
i).device.equals(
device)) {
163 Entry e = entries.get(
i);
171 Entry e =
new Entry();
174 e.isConnected =
true;
180 Entry e =
new Entry();
183 e.isConnected =
true;
184 List<Entry>
list =
new LinkedList<Entry>();
186 notificationStore.put(characteristic,
list);
195 public void markDeviceConnectivity(BluetoothDevice
device,
boolean isConnected)
197 final Iterator<BluetoothGattCharacteristic>
keys = notificationStore.keySet().iterator();
198 while (
keys.hasNext()) {
199 final BluetoothGattCharacteristic characteristic =
keys.next();
200 final List<Entry> entries = notificationStore.get(characteristic);
204 ListIterator<Entry> charConfig = entries.listIterator();
205 while (charConfig.hasNext()) {
206 Entry e = charConfig.next();
207 if (e.device.equals(
device))
208 e.isConnected = isConnected;
215 List<BluetoothDevice> getToBeUpdatedDevices(BluetoothGattCharacteristic characteristic)
217 ArrayList<BluetoothDevice>
result =
new ArrayList<BluetoothDevice>();
218 if (!notificationStore.containsKey(characteristic))
221 final ListIterator<Entry>
iter = notificationStore.get(characteristic).listIterator();
222 while (
iter.hasNext())
230 byte[] valueFor(BluetoothGattCharacteristic characteristic, BluetoothDevice
device)
232 if (!notificationStore.containsKey(characteristic))
235 List<Entry> entries = notificationStore.get(characteristic);
236 for (
int i = 0;
i < entries.size();
i++) {
237 final Entry
entry = entries.get(
i);
239 return entries.get(
i).value;
246 private static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_UUID = UUID
247 .fromString(
"00002902-0000-1000-8000-00805f9b34fb");
248 ClientCharacteristicManager clientCharacteristicManager =
new ClientCharacteristicManager();
253 if (qtContext ==
null) {
254 Log.w(
TAG,
"Missing context object. Peripheral role disabled.");
259 (BluetoothManager) qtContext.getSystemService(Context.BLUETOOTH_SERVICE);
260 if (mBluetoothManager ==
null) {
261 Log.w(
TAG,
"Bluetooth service not available. Peripheral role disabled.");
265 mBluetoothAdapter = mBluetoothManager.getAdapter();
266 if (mBluetoothAdapter ==
null) {
267 Log.w(
TAG,
"Missing Bluetooth adapter. Peripheral role disabled.");
271 Log.w(
TAG,
"Let's do BTLE Peripheral.");
290 if (mGattServer ==
null) {
291 Log.w(
TAG,
"Ignoring connection state event, server is disconnected");
299 List<BluetoothDevice> connectedDevices =
300 mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
302 + status +
", connected devices: " + connectedDevices);
305 int qtControllerState = connectedDevices.size() > 0 ? 2 : 0;
308 case BluetoothProfile.STATE_CONNECTED:
309 clientCharacteristicManager.markDeviceConnectivity(
device,
true);
310 mRemoteName =
device.getName();
311 mRemoteAddress =
device.getAddress();
313 case BluetoothProfile.STATE_DISCONNECTED:
314 clientCharacteristicManager.markDeviceConnectivity(
device,
false);
315 clearPendingPreparedWrites(
device);
317 if (
device.getAddress().equals(mRemoteAddress)
318 && !connectedDevices.isEmpty()) {
319 mRemoteName = connectedDevices.get(0).getName();
320 mRemoteAddress = connectedDevices.get(0).getAddress();
325 Log.w(
TAG,
"Unhandled connection state change: " +
newState);
330 if (qtControllerState == 0) {
331 mPendingServiceAdditions.clear();
336 mSupportedMtu = DEFAULT_LE_ATT_MTU;
341 case BluetoothGatt.GATT_SUCCESS:
345 Log.w(
TAG,
"Unhandled error code on peripheral connectionStateChanged: "
347 qtErrorCode = status;
356 if (mGattServer ==
null) {
357 Log.w(
TAG,
"Ignoring service addition event, server is disconnected");
361 Log.d(
TAG,
"Service " + service.getUuid().toString() +
" addition result: " + status);
364 ListIterator<BluetoothGattService> iterator = mPendingServiceAdditions.listIterator();
365 while (iterator.hasNext()) {
366 if (iterator.next().getUuid().equals(service.getUuid())) {
373 iterator = mPendingServiceAdditions.listIterator();
374 while (iterator.hasNext()) {
375 BluetoothGattService nextService = iterator.next();
376 if (mGattServer.addService(nextService)) {
379 Log.w(
TAG,
"Adding service " + nextService.getUuid().toString() +
" failed");
387 BluetoothGattCharacteristic characteristic)
389 if (mGattServer ==
null) {
390 Log.w(
TAG,
"Ignoring characteristic read, server is disconnected");
394 byte[] characteristicData =
398 byte[] dataArray = Arrays.copyOfRange(characteristicData,
399 offset, characteristicData.length);
400 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS,
402 }
catch (Exception ex) {
403 Log.w(
TAG,
"onCharacteristicReadRequest: " +
requestId +
" "
404 +
offset +
" " + characteristicData.length);
405 ex.printStackTrace();
406 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_FAILURE,
413 BluetoothGattCharacteristic characteristic,
414 boolean preparedWrite,
boolean responseNeeded,
417 if (mGattServer ==
null) {
418 Log.w(
TAG,
"Ignoring characteristic write, server is disconnected");
421 Log.w(
TAG,
"onCharacteristicWriteRequest " + preparedWrite +
" " +
offset +
" "
426 int resultStatus = BluetoothGatt.GATT_SUCCESS;
427 boolean sendNotificationOrIndication =
false;
429 if (!preparedWrite) {
433 if (
value.length < minValueLen ||
value.length > maxValueLen) {
435 Log.w(
TAG,
"onCharacteristicWriteRequest invalid char value length: "
436 +
value.length +
", min: " + minValueLen +
", max: " + maxValueLen);
437 resultStatus = BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
441 sendNotificationOrIndication =
true;
444 Log.w(
TAG,
"onCharacteristicWriteRequest: !preparedWrite, offset "
445 +
offset +
", Not supported");
446 resultStatus = BluetoothGatt.GATT_INVALID_OFFSET;
459 if (sendNotificationOrIndication)
460 sendNotificationsOrIndications(characteristic);
464 int offset, BluetoothGattDescriptor descriptor)
466 if (mGattServer ==
null) {
467 Log.w(
TAG,
"Ignoring descriptor read, server is disconnected");
474 if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
475 dataArray = clientCharacteristicManager.valueFor(
476 descriptor.getCharacteristic(),
device);
477 if (dataArray ==
null)
481 dataArray = Arrays.copyOfRange(dataArray,
offset, dataArray.length);
482 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS,
484 }
catch (Exception ex) {
485 Log.w(
TAG,
"onDescriptorReadRequest: " +
requestId +
" "
486 +
offset +
" " + dataArray.length);
487 ex.printStackTrace();
488 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_FAILURE,
494 BluetoothGattDescriptor descriptor,
boolean preparedWrite,
497 if (mGattServer ==
null) {
498 Log.w(
TAG,
"Ignoring descriptor write, server is disconnected");
502 Log.w(
TAG,
"onDescriptorWriteRequest " + preparedWrite +
" " +
offset +
" " +
value.length);
503 int resultStatus = BluetoothGatt.GATT_SUCCESS;
505 if (!preparedWrite) {
507 if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
515 if (
value[0] == 0x03) {
516 Log.w(
TAG,
"Warning: In CCC of characteristic: "
517 + descriptor.getCharacteristic().getUuid()
518 +
" enabling both NTF & IND requested, enabling NTF only.");
519 value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
521 clientCharacteristicManager.insertOrUpdate(
522 descriptor.getCharacteristic(),
529 Log.w(
TAG,
"onDescriptorWriteRequest: !preparedWrite, offset "
530 +
offset +
", Not supported");
531 resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED;
549 if (mGattServer ==
null) {
550 Log.w(
TAG,
"Ignoring execute write, server is disconnected");
559 for (WriteEntry
entry : mPendingPreparedWrites) {
563 byte[] newValue =
null;
565 byte[]
currentValue = (entry.target instanceof BluetoothGattCharacteristic)
570 for (Pair<
byte[], Integer>
write :
entry.writes) {
574 clearPendingPreparedWrites(
device);
577 BluetoothGatt.GATT_INVALID_OFFSET,
588 (
write.second.intValue() +
write.first.length >
590 clearPendingPreparedWrites(
device);
593 BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH,
600 newValue =
new byte[Math.max(
write.second.intValue() +
606 System.arraycopy(
write.first, 0, newValue,
write.second.intValue(),
613 if (
entry.target instanceof BluetoothGattCharacteristic) {
616 qtObject, (BluetoothGattCharacteristic)
entry.target, newValue);
620 qtObject, (BluetoothGattDescriptor)
entry.target, newValue);
626 clearPendingPreparedWrites(
device);
627 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS, 0,
null);
632 if (mSupportedMtu ==
mtu)
641 private BluetoothGattServerCallback mGattServerListener =
new BluetoothGattServerCallback()
644 public void onConnectionStateChange(BluetoothDevice
device,
int status,
int newState) {
650 public void onServiceAdded(
int status, BluetoothGattService service) {
651 super.onServiceAdded(status, service);
656 public void onCharacteristicReadRequest(BluetoothDevice
device,
int requestId,
int offset, BluetoothGattCharacteristic characteristic)
663 public void onCharacteristicWriteRequest(BluetoothDevice
device,
int requestId, BluetoothGattCharacteristic characteristic,
664 boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[]
value)
673 public void onDescriptorReadRequest(BluetoothDevice
device,
int requestId,
int offset, BluetoothGattDescriptor descriptor)
680 public void onDescriptorWriteRequest(BluetoothDevice
device,
int requestId, BluetoothGattDescriptor descriptor,
681 boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[]
value)
683 super.onDescriptorWriteRequest(
device,
requestId, descriptor, preparedWrite,
690 public void onExecuteWrite(BluetoothDevice
device,
int requestId,
boolean execute)
697 public void onNotificationSent(BluetoothDevice
device,
int status) {
698 super.onNotificationSent(
device, status);
699 Log.w(TAG,
"onNotificationSent" +
device +
" " + status);
703 public void onMtuChanged(BluetoothDevice
device,
int mtu) {
709 public synchronized int mtu() {
710 return mSupportedMtu;
716 if (mGattServer !=
null)
719 BluetoothManager
manager = (BluetoothManager) qtContext.getSystemService(Context.BLUETOOTH_SERVICE);
721 Log.w(
TAG,
"Bluetooth service not available.");
725 mGattServer =
manager.openGattServer(qtContext, mGattServerListener);
727 return (mGattServer !=
null);
733 if (mGattServer ==
null)
736 clearPendingPreparedWrites(
null);
737 mPendingServiceAdditions.clear();
741 mRemoteName = mRemoteAddress =
"";
748 AdvertiseData scanResponse,
752 if (mBluetoothAdapter ==
null || !mBluetoothAdapter.isEnabled()) {
753 Log.w(
TAG,
"StartAdvertising: Bluetooth not available or offline");
758 if (mLeAdvertiser ==
null && mBluetoothAdapter.isMultipleAdvertisementSupported())
759 mLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
761 if (mLeAdvertiser ==
null) {
762 Log.w(
TAG,
"StartAdvertising: LE advertisement not supported");
767 Log.w(
TAG,
"Server::startAdvertising: Cannot open GATT server");
771 Log.w(
TAG,
"Starting to advertise.");
772 mLeAdvertiser.startAdvertising(
settings, advertiseData, scanResponse, mAdvertiseListener);
780 if (mLeAdvertiser ==
null)
783 mLeAdvertiser.stopAdvertising(mAdvertiseListener);
784 Log.w(
TAG,
"Advertisement stopped.");
788 public synchronized void addService(BluetoothGattService service)
791 Log.w(
TAG,
"Server::addService: Cannot open GATT server");
799 if (mPendingServiceAdditions.isEmpty()) {
800 if (mGattServer.addService(service))
801 mPendingServiceAdditions.add(service);
803 Log.w(
TAG,
"Adding service " + service.getUuid().toString() +
" failed.");
805 mPendingServiceAdditions.add(service);
815 private void sendNotificationsOrIndications(BluetoothGattCharacteristic characteristic)
817 final ListIterator<BluetoothDevice>
iter =
818 clientCharacteristicManager.getToBeUpdatedDevices(characteristic).listIterator();
824 while (
iter.hasNext()) {
826 final byte[] clientCharacteristicConfig =
827 clientCharacteristicManager.valueFor(characteristic,
device);
828 if (clientCharacteristicConfig !=
null) {
829 if (Arrays.equals(clientCharacteristicConfig,
830 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
831 if (Build.VERSION.SDK_INT >= 33) {
832 mGattServer.notifyCharacteristicChanged(
device, characteristic,
false,
835 mGattServer.notifyCharacteristicChanged(
device, characteristic,
false);
837 }
else if (Arrays.equals(clientCharacteristicConfig,
838 BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
839 if (Build.VERSION.SDK_INT >= 33) {
840 mGattServer.notifyCharacteristicChanged(
device, characteristic,
true,
841 ((QtBluetoothGattCharacteristic)characteristic).getLocalValue());
843 mGattServer.notifyCharacteristicChanged(
device, characteristic,
true);
859 BluetoothGattCharacteristic foundChar =
null;
860 List<BluetoothGattCharacteristic> charList = service.getCharacteristics();
861 for (BluetoothGattCharacteristic
iter: charList) {
862 if (
iter.getUuid().equals(charUuid) && foundChar ==
null) {
865 }
else if (
iter.getUuid().equals(charUuid)) {
866 Log.w(
TAG,
"Found second char with same UUID. Wrong char may have been selected.");
871 if (foundChar ==
null) {
872 Log.w(
TAG,
"writeCharacteristic: update for unknown characteristic failed");
881 if (newValue.length < minValueLength || newValue.length > maxValueLength) {
882 Log.w(
TAG,
"writeCharacteristic: invalid value length: "
883 + newValue.length +
", min: " + minValueLength +
", max: " + maxValueLength);
891 if (mGattServer !=
null)
892 sendNotificationsOrIndications(foundChar);
903 public boolean writeDescriptor(BluetoothGattService service, UUID charUuid, UUID descUuid,
906 BluetoothGattDescriptor foundDesc =
null;
907 BluetoothGattCharacteristic foundChar =
null;
908 final List<BluetoothGattCharacteristic> charList = service.getCharacteristics();
909 for (BluetoothGattCharacteristic
iter: charList) {
910 if (!
iter.getUuid().equals(charUuid))
913 if (foundChar ==
null) {
916 Log.w(
TAG,
"Found second char with same UUID. Wrong char may have been selected.");
921 if (foundChar !=
null)
922 foundDesc = foundChar.getDescriptor(descUuid);
924 if (foundChar ==
null || foundDesc ==
null) {
925 Log.w(
TAG,
"writeDescriptor: update for unknown char or desc failed (" + foundChar +
")");
942 private AdvertiseCallback mAdvertiseListener =
new AdvertiseCallback()
945 public void onStartSuccess(AdvertiseSettings settingsInEffect) {
946 super.onStartSuccess(settingsInEffect);
950 public void onStartFailure(
int errorCode) {
951 Log.e(TAG,
"Advertising failure: " + errorCode);
952 super.onStartFailure(errorCode);
957 case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
959 case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
960 Log.e(TAG,
"Please reduce size of advertising data.");
963 case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
967 case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
970 case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
984 BluetoothGattCharacteristic characteristic,
987 BluetoothGattDescriptor descriptor,