4package org.qtproject.qt.android.bluetooth;
6import android.bluetooth.BluetoothDevice;
7import android.bluetooth.BluetoothGattCharacteristic;
8import android.bluetooth.BluetoothGattDescriptor;
9import android.bluetooth.BluetoothGattService;
10import android.content.Context;
11import android.bluetooth.BluetoothAdapter;
12import android.bluetooth.BluetoothGatt;
13import android.bluetooth.BluetoothGattServer;
14import android.bluetooth.BluetoothGattServerCallback;
15import android.bluetooth.BluetoothManager;
16import android.bluetooth.BluetoothProfile;
17import android.bluetooth.le.AdvertiseCallback;
18import android.bluetooth.le.AdvertiseData;
19import android.bluetooth.le.AdvertiseSettings;
20import android.bluetooth.le.BluetoothLeAdvertiser;
21import android.os.Build;
22import android.util.Log;
23import android.util.Pair;
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.Iterator;
28import java.util.LinkedList;
30import java.util.ListIterator;
31import java.util.HashMap;
34class QtBluetoothLEServer {
35 private static final String TAG =
"QtBluetoothGattServer";
38 @SuppressWarnings({
"CanBeFinal",
"WeakerAccess"})
40 @SuppressWarnings(
"WeakerAccess")
46 private BluetoothManager mBluetoothManager = null;
47 private BluetoothGattServer mGattServer = null;
48 private BluetoothLeAdvertiser mLeAdvertiser = null;
50 private ArrayList<BluetoothGattService> mPendingServiceAdditions =
51 new ArrayList<BluetoothGattService>();
55 synchronized
String remoteName() {
59 private String mRemoteAddress =
"";
61 synchronized String remoteAddress() {
62 return mRemoteAddress;
66 private static final int DEFAULT_LE_ATT_MTU = 23;
68 private int mSupportedMtu = DEFAULT_LE_ATT_MTU;
70 private static final int MAX_PENDING_WRITE_COUNT = 1024;
72 private static final int GATT_ERROR_PREPARE_QUEUE_FULL = 0x9;
74 private static final int BTLE_MAX_ATTRIBUTE_VALUE_SIZE = 512;
78 private class WriteEntry {
80 this.remoteDevice = remoteDevice;
82 this.writes =
new ArrayList<Pair<byte[], Integer>>();
86 return remoteDevice.equals(
device) && target.equals(target);
96 private void clearPendingPreparedWrites(Object
device) {
98 mPendingPreparedWrites.clear();
99 ListIterator<WriteEntry>
iterator = mPendingPreparedWrites.listIterator();
111 WriteEntry
entry =
null;
112 int currentWriteCount = 0;
117 for (WriteEntry e : mPendingPreparedWrites) {
120 currentWriteCount += e.writes.size();
124 if (currentWriteCount > MAX_PENDING_WRITE_COUNT) {
125 Log.w(TAG,
"Prepared write queue is full, returning an error.");
126 return GATT_ERROR_PREPARE_QUEUE_FULL;
136 return BluetoothGatt.GATT_SUCCESS;
144 private class ClientCharacteristicManager {
145 private final HashMap<BluetoothGattCharacteristic, List<Entry>> notificationStore =
new HashMap<BluetoothGattCharacteristic, List<Entry>>();
147 private class Entry {
150 boolean isConnected =
false;
153 void insertOrUpdate(BluetoothGattCharacteristic characteristic,
156 if (notificationStore.containsKey(characteristic)) {
158 List<Entry> entries = notificationStore.get(characteristic);
159 for (
int i = 0;
i < entries.size();
i++) {
160 if (entries.get(i).device.equals(
device)) {
161 Entry e = entries.get(i);
169 Entry e =
new Entry();
172 e.isConnected =
true;
178 Entry e =
new Entry();
181 e.isConnected =
true;
184 notificationStore.put(characteristic,
list);
195 final Iterator<BluetoothGattCharacteristic> keys = notificationStore.keySet().
iterator();
196 while (keys.hasNext()) {
197 final BluetoothGattCharacteristic characteristic = keys.next();
198 final List<Entry> entries = notificationStore.get(characteristic);
202 ListIterator<Entry> charConfig = entries.listIterator();
203 while (charConfig.hasNext()) {
204 Entry e = charConfig.next();
205 if (e.device.equals(
device))
206 e.isConnected = isConnected;
215 ArrayList<BluetoothDevice>
result =
new ArrayList<BluetoothDevice>();
216 if (!notificationStore.containsKey(characteristic))
219 final ListIterator<Entry>
iter = notificationStore.get(characteristic).listIterator();
220 while (
iter.hasNext())
230 if (!notificationStore.containsKey(characteristic))
233 List<Entry> entries = notificationStore.get(characteristic);
234 for (
int i = 0;
i < entries.size();
i++) {
235 final Entry
entry = entries.get(i);
237 return entries.get(i).value;
244 private static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_UUID = UUID
245 .fromString(
"00002902-0000-1000-8000-00805f9b34fb");
246 ClientCharacteristicManager clientCharacteristicManager =
new ClientCharacteristicManager();
251 if (qtContext ==
null) {
252 Log.w(TAG,
"Missing context object. Peripheral role disabled.");
257 (BluetoothManager) qtContext.getSystemService(
Context.BLUETOOTH_SERVICE);
258 if (mBluetoothManager ==
null) {
259 Log.w(TAG,
"Bluetooth service not available. Peripheral role disabled.");
263 mBluetoothAdapter = mBluetoothManager.getAdapter();
264 if (mBluetoothAdapter ==
null) {
265 Log.w(TAG,
"Missing Bluetooth adapter. Peripheral role disabled.");
269 Log.w(TAG,
"Let's do BTLE Peripheral.");
288 if (mGattServer ==
null) {
289 Log.w(TAG,
"Ignoring connection state event, server is disconnected");
298 mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
300 + status +
", connected devices: " + connectedDevices);
303 int qtControllerState = connectedDevices.
size() > 0 ? 2 : 0;
306 case BluetoothProfile.STATE_CONNECTED:
307 clientCharacteristicManager.markDeviceConnectivity(
device,
true);
308 mRemoteName =
device.getName();
309 mRemoteAddress =
device.getAddress();
311 case BluetoothProfile.STATE_DISCONNECTED:
312 clientCharacteristicManager.markDeviceConnectivity(
device,
false);
313 clearPendingPreparedWrites(
device);
315 if (
device.getAddress().equals(mRemoteAddress)
316 && !connectedDevices.isEmpty()) {
317 mRemoteName = connectedDevices.get(0).getName();
318 mRemoteAddress = connectedDevices.get(0).getAddress();
323 Log.w(TAG,
"Unhandled connection state change: " +
newState);
328 if (qtControllerState == 0) {
329 mPendingServiceAdditions.clear();
334 mSupportedMtu = DEFAULT_LE_ATT_MTU;
339 case BluetoothGatt.GATT_SUCCESS:
343 Log.w(TAG,
"Unhandled error code on peripheral connectionStateChanged: "
345 qtErrorCode = status;
349 leConnectionStateChange(qtObject, qtErrorCode, qtControllerState);
352 synchronized void handleOnServiceAdded(
int status, BluetoothGattService service)
354 if (mGattServer ==
null) {
355 Log.w(TAG,
"Ignoring service addition event, server is disconnected");
359 Log.d(TAG,
"Service " +
service.getUuid().toString() +
" addition result: " + status);
362 ListIterator<BluetoothGattService>
iterator = mPendingServiceAdditions.listIterator();
371 iterator = mPendingServiceAdditions.listIterator();
373 BluetoothGattService nextService =
iterator.next();
374 if (mGattServer.addService(nextService)) {
377 Log.w(TAG,
"Adding service " + nextService.getUuid().toString() +
" failed");
385 BluetoothGattCharacteristic characteristic)
387 if (mGattServer ==
null) {
388 Log.w(TAG,
"Ignoring characteristic read, server is disconnected");
392 byte[] characteristicData =
393 ((QtBluetoothGattCharacteristic)characteristic).getLocalValue();
396 byte[] dataArray = Arrays.copyOfRange(characteristicData,
397 offset, characteristicData.length);
398 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS,
400 }
catch (Exception ex) {
401 Log.w(TAG,
"onCharacteristicReadRequest: " +
requestId +
" "
402 +
offset +
" " + characteristicData.length);
403 ex.printStackTrace();
404 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_FAILURE,
411 BluetoothGattCharacteristic characteristic,
412 boolean preparedWrite,
boolean responseNeeded,
415 if (mGattServer ==
null) {
416 Log.w(TAG,
"Ignoring characteristic write, server is disconnected");
419 Log.w(TAG,
"onCharacteristicWriteRequest " + preparedWrite +
" " +
offset +
" "
421 final int minValueLen = ((QtBluetoothGattCharacteristic)characteristic).minValueLength;
422 final int maxValueLen = ((QtBluetoothGattCharacteristic)characteristic).maxValueLength;
424 int resultStatus = BluetoothGatt.GATT_SUCCESS;
425 boolean sendNotificationOrIndication =
false;
427 if (!preparedWrite) {
431 if (
value.length < minValueLen ||
value.length > maxValueLen) {
433 Log.w(TAG,
"onCharacteristicWriteRequest invalid char value length: "
434 +
value.length +
", min: " + minValueLen +
", max: " + maxValueLen);
435 resultStatus = BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
437 ((QtBluetoothGattCharacteristic)characteristic).setLocalValue(
value);
438 leServerCharacteristicChanged(qtObject, characteristic,
value);
439 sendNotificationOrIndication =
true;
442 Log.w(TAG,
"onCharacteristicWriteRequest: !preparedWrite, offset "
443 +
offset +
", Not supported");
444 resultStatus = BluetoothGatt.GATT_INVALID_OFFSET;
457 if (sendNotificationOrIndication)
458 sendNotificationsOrIndications(characteristic);
462 int offset, BluetoothGattDescriptor descriptor)
464 if (mGattServer ==
null) {
465 Log.w(TAG,
"Ignoring descriptor read, server is disconnected");
469 byte[] dataArray = ((QtBluetoothGattDescriptor)descriptor).getLocalValue();
472 if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
473 dataArray = clientCharacteristicManager.valueFor(
474 descriptor.getCharacteristic(),
device);
475 if (dataArray ==
null)
476 dataArray = ((QtBluetoothGattDescriptor)descriptor).getLocalValue();
479 dataArray = Arrays.copyOfRange(dataArray,
offset, dataArray.length);
480 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS,
482 }
catch (Exception ex) {
483 Log.w(TAG,
"onDescriptorReadRequest: " +
requestId +
" "
484 +
offset +
" " + dataArray.length);
485 ex.printStackTrace();
486 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_FAILURE,
492 BluetoothGattDescriptor descriptor,
boolean preparedWrite,
495 if (mGattServer ==
null) {
496 Log.w(TAG,
"Ignoring descriptor write, server is disconnected");
500 Log.w(TAG,
"onDescriptorWriteRequest " + preparedWrite +
" " +
offset +
" " +
value.length);
501 int resultStatus = BluetoothGatt.GATT_SUCCESS;
503 if (!preparedWrite) {
505 if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
513 if (
value[0] == 0x03) {
514 Log.w(TAG,
"Warning: In CCC of characteristic: "
515 + descriptor.getCharacteristic().getUuid()
516 +
" enabling both NTF & IND requested, enabling NTF only.");
517 value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
519 clientCharacteristicManager.insertOrUpdate(
520 descriptor.getCharacteristic(),
523 ((QtBluetoothGattDescriptor)descriptor).setLocalValue(
value);
524 leServerDescriptorWritten(qtObject, descriptor,
value);
527 Log.w(TAG,
"onDescriptorWriteRequest: !preparedWrite, offset "
528 +
offset +
", Not supported");
529 resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED;
547 if (mGattServer ==
null) {
548 Log.w(TAG,
"Ignoring execute write, server is disconnected");
557 for (WriteEntry
entry : mPendingPreparedWrites) {
561 byte[] newValue =
null;
563 byte[] currentValue = (entry.target instanceof BluetoothGattCharacteristic)
564 ? ((QtBluetoothGattCharacteristic)
entry.target).getLocalValue()
565 : ((QtBluetoothGattDescriptor)
entry.target).getLocalValue();
571 if (
write.second.intValue() > currentValue.length) {
572 clearPendingPreparedWrites(
device);
575 BluetoothGatt.GATT_INVALID_OFFSET,
585 if (
entry.target instanceof QtBluetoothGattCharacteristic &&
586 (
write.second.intValue() +
write.first.length >
587 ((QtBluetoothGattCharacteristic)
entry.target).maxValueLength)) {
588 clearPendingPreparedWrites(
device);
591 BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH,
598 newValue =
new byte[Math.max(
write.second.intValue() +
599 write.first.length, currentValue.length)];
602 System.arraycopy(currentValue, 0, newValue, 0, currentValue.length);
607 currentValue = newValue;
611 if (
entry.target instanceof BluetoothGattCharacteristic) {
612 ((QtBluetoothGattCharacteristic)
entry.target).setLocalValue(newValue);
613 leServerCharacteristicChanged(
614 qtObject, (BluetoothGattCharacteristic)
entry.target, newValue);
616 ((QtBluetoothGattDescriptor)
entry.target).setLocalValue(newValue);
617 leServerDescriptorWritten(
618 qtObject, (BluetoothGattDescriptor)
entry.target, newValue);
624 clearPendingPreparedWrites(
device);
625 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS, 0,
null);
630 if (mSupportedMtu == mtu)
633 leMtuChanged(qtObject, mSupportedMtu);
639 private BluetoothGattServerCallback mGattServerListener =
new BluetoothGattServerCallback()
648 public void onServiceAdded(
int status, BluetoothGattService service) {
649 super.onServiceAdded(status, service);
650 handleOnServiceAdded(status, service);
662 boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[]
value)
679 boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[]
value)
681 super.onDescriptorWriteRequest(
device,
requestId, descriptor, preparedWrite,
683 handleOnDescriptorWriteRequest(
device,
requestId, descriptor, preparedWrite,
696 super.onNotificationSent(
device, status);
697 Log.w(TAG,
"onNotificationSent" +
device +
" " + status);
702 handleOnMtuChanged(
device, mtu);
707 synchronized int mtu() {
708 return mSupportedMtu;
712 synchronized boolean connectServer()
714 if (mGattServer !=
null)
717 BluetoothManager
manager = (BluetoothManager) qtContext.getSystemService(
Context.BLUETOOTH_SERVICE);
719 Log.w(TAG,
"Bluetooth service not available.");
723 mGattServer =
manager.openGattServer(qtContext, mGattServerListener);
725 return (mGattServer !=
null);
729 synchronized void disconnectServer()
731 if (mGattServer ==
null)
734 clearPendingPreparedWrites(
null);
735 mPendingServiceAdditions.clear();
739 mRemoteName = mRemoteAddress =
"";
740 leConnectionStateChange(qtObject, 0 ,
745 boolean startAdvertising(AdvertiseData advertiseData,
746 AdvertiseData scanResponse,
750 if (mBluetoothAdapter ==
null || !mBluetoothAdapter.isEnabled()) {
751 Log.w(TAG,
"StartAdvertising: Bluetooth not available or offline");
756 if (mLeAdvertiser ==
null && mBluetoothAdapter.isMultipleAdvertisementSupported())
757 mLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
759 if (mLeAdvertiser ==
null) {
760 Log.w(TAG,
"StartAdvertising: LE advertisement not supported");
764 if (!connectServer()) {
765 Log.w(TAG,
"Server::startAdvertising: Cannot open GATT server");
769 Log.w(TAG,
"Starting to advertise.");
770 mLeAdvertiser.startAdvertising(
settings, advertiseData, scanResponse, mAdvertiseListener);
776 void stopAdvertising()
778 if (mLeAdvertiser ==
null)
781 mLeAdvertiser.stopAdvertising(mAdvertiseListener);
782 Log.w(TAG,
"Advertisement stopped.");
786 synchronized void addService(BluetoothGattService service)
788 if (!connectServer()) {
789 Log.w(TAG,
"Server::addService: Cannot open GATT server");
797 if (mPendingServiceAdditions.isEmpty()) {
798 if (mGattServer.addService(service))
799 mPendingServiceAdditions.add(service);
801 Log.w(TAG,
"Adding service " +
service.getUuid().toString() +
" failed.");
803 mPendingServiceAdditions.add(service);
808 @SuppressWarnings(
"deprecation")
810 BluetoothGattCharacteristic characteristic,
813 mGattServer.notifyCharacteristicChanged(
device, characteristic, confirm);
822 private void sendNotificationsOrIndications(BluetoothGattCharacteristic characteristic)
824 final ListIterator<BluetoothDevice>
iter =
825 clientCharacteristicManager.getToBeUpdatedDevices(characteristic).listIterator();
831 while (
iter.hasNext()) {
833 final byte[] clientCharacteristicConfig =
834 clientCharacteristicManager.valueFor(characteristic,
device);
835 if (clientCharacteristicConfig !=
null) {
836 if (Arrays.equals(clientCharacteristicConfig,
837 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
838 if (Build.VERSION.SDK_INT >= 33) {
839 mGattServer.notifyCharacteristicChanged(
device, characteristic,
false,
840 ((QtBluetoothGattCharacteristic)characteristic).getLocalValue());
842 notifyCharacteristicChange(
device, characteristic,
false);
844 }
else if (Arrays.equals(clientCharacteristicConfig,
845 BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
846 if (Build.VERSION.SDK_INT >= 33) {
847 mGattServer.notifyCharacteristicChanged(
device, characteristic,
true,
848 ((QtBluetoothGattCharacteristic)characteristic).getLocalValue());
850 notifyCharacteristicChange(
device, characteristic,
true);
864 boolean writeCharacteristic(BluetoothGattService service, UUID charUuid,
byte[] newValue)
866 BluetoothGattCharacteristic foundChar =
null;
868 for (BluetoothGattCharacteristic
iter: charList) {
869 if (
iter.getUuid().equals(charUuid) && foundChar ==
null) {
872 }
else if (
iter.getUuid().equals(charUuid)) {
873 Log.w(TAG,
"Found second char with same UUID. Wrong char may have been selected.");
878 if (foundChar ==
null) {
879 Log.w(TAG,
"writeCharacteristic: update for unknown characteristic failed");
886 final int minValueLength = ((QtBluetoothGattCharacteristic)foundChar).minValueLength;
887 final int maxValueLength = ((QtBluetoothGattCharacteristic)foundChar).maxValueLength;
888 if (newValue.length < minValueLength || newValue.length > maxValueLength) {
889 Log.w(TAG,
"writeCharacteristic: invalid value length: "
890 + newValue.length +
", min: " + minValueLength +
", max: " + maxValueLength);
896 ((QtBluetoothGattCharacteristic)foundChar).setLocalValue(newValue);
898 if (mGattServer !=
null)
899 sendNotificationsOrIndications(foundChar);
910 boolean writeDescriptor(BluetoothGattService service, UUID charUuid, UUID descUuid,
913 BluetoothGattDescriptor foundDesc =
null;
914 BluetoothGattCharacteristic foundChar =
null;
916 for (BluetoothGattCharacteristic
iter: charList) {
917 if (!
iter.getUuid().equals(charUuid))
920 if (foundChar ==
null) {
923 Log.w(TAG,
"Found second char with same UUID. Wrong char may have been selected.");
928 if (foundChar !=
null)
929 foundDesc = foundChar.getDescriptor(descUuid);
931 if (foundChar ==
null || foundDesc ==
null) {
932 Log.w(TAG,
"writeDescriptor: update for unknown char or desc failed (" + foundChar +
")");
940 ((QtBluetoothGattDescriptor)foundDesc).setLocalValue(newValue);
949 private AdvertiseCallback mAdvertiseListener =
new AdvertiseCallback()
952 public void onStartSuccess(AdvertiseSettings settingsInEffect) {
953 super.onStartSuccess(settingsInEffect);
957 public void onStartFailure(
int errorCode) {
958 Log.e(TAG,
"Advertising failure: " + errorCode);
959 super.onStartFailure(errorCode);
964 case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
966 case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
967 Log.e(TAG,
"Please reduce size of advertising data.");
970 case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
974 case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
977 case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
983 leServerAdvertisementError(qtObject, qtErrorCode);
987 native
void leConnectionStateChange(
long qtObject,
int errorCode,
int newState);
988 native
void leMtuChanged(
long qtObject,
int mtu);
989 native
void leServerAdvertisementError(
long qtObject,
int status);
990 native
void leServerCharacteristicChanged(
long qtObject,
991 BluetoothGattCharacteristic characteristic,
993 native
void leServerDescriptorWritten(
long qtObject,
994 BluetoothGattDescriptor descriptor,
IOBluetoothDevice * device
constexpr qsizetype size() const noexcept
Returns the number of characters in this string.
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
static const QString context()
typename C::iterator iterator
Q_CORE_EXPORT QtJniTypes::Service service()
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
EGLOutputLayerEXT EGLint EGLAttrib value
[3]
QNearFieldTarget::RequestId requestId
GLenum GLuint GLintptr offset
QNetworkAccessManager manager
[0]
socket write("GET / HTTP/1.0\r\n\r\n")