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.AdvertisingSet;
18import android.bluetooth.le.AdvertisingSetCallback;
19import android.bluetooth.le.AdvertisingSetParameters;
20import android.bluetooth.le.AdvertiseData;
21import android.bluetooth.le.BluetoothLeAdvertiser;
22import android.os.Build;
23import android.util.Log;
24import android.util.Pair;
26import java.util.ArrayList;
27import java.util.Arrays;
28import java.util.Iterator;
29import java.util.LinkedList;
31import java.util.ListIterator;
32import java.util.HashMap;
35class QtBluetoothLEServer {
36 private static final String TAG =
"QtBluetoothGattServer";
39 @SuppressWarnings({
"CanBeFinal",
"WeakerAccess"})
41 @SuppressWarnings(
"WeakerAccess")
47 private BluetoothManager mBluetoothManager = null;
48 private BluetoothGattServer mGattServer = null;
49 private BluetoothLeAdvertiser mLeAdvertiser = null;
51 private ArrayList<BluetoothGattService> mPendingServiceAdditions =
52 new ArrayList<BluetoothGattService>();
56 synchronized
String remoteName() {
60 private String mRemoteAddress =
"";
62 synchronized String remoteAddress() {
63 return mRemoteAddress;
67 private static final int DEFAULT_LE_ATT_MTU = 23;
69 private int mSupportedMtu = DEFAULT_LE_ATT_MTU;
71 private static final int MAX_PENDING_WRITE_COUNT = 1024;
73 private static final int GATT_ERROR_PREPARE_QUEUE_FULL = 0x9;
75 private static final int BTLE_MAX_ATTRIBUTE_VALUE_SIZE = 512;
79 private class WriteEntry {
81 this.remoteDevice = remoteDevice;
83 this.writes =
new ArrayList<Pair<byte[], Integer>>();
87 return remoteDevice.equals(
device) && target.equals(target);
97 private void clearPendingPreparedWrites(Object
device) {
99 mPendingPreparedWrites.clear();
100 ListIterator<WriteEntry>
iterator = mPendingPreparedWrites.listIterator();
112 WriteEntry
entry =
null;
113 int currentWriteCount = 0;
118 for (WriteEntry e : mPendingPreparedWrites) {
121 currentWriteCount += e.writes.size();
125 if (currentWriteCount > MAX_PENDING_WRITE_COUNT) {
126 Log.w(TAG,
"Prepared write queue is full, returning an error.");
127 return GATT_ERROR_PREPARE_QUEUE_FULL;
137 return BluetoothGatt.GATT_SUCCESS;
145 private class ClientCharacteristicManager {
146 private final HashMap<BluetoothGattCharacteristic, List<Entry>> notificationStore =
new HashMap<BluetoothGattCharacteristic, List<Entry>>();
148 private class Entry {
151 boolean isConnected =
false;
154 void insertOrUpdate(BluetoothGattCharacteristic characteristic,
157 if (notificationStore.containsKey(characteristic)) {
159 List<Entry> entries = notificationStore.get(characteristic);
160 for (
int i = 0;
i < entries.size();
i++) {
161 if (entries.get(i).device.equals(
device)) {
162 Entry e = entries.get(i);
170 Entry e =
new Entry();
173 e.isConnected =
true;
179 Entry e =
new Entry();
182 e.isConnected =
true;
185 notificationStore.put(characteristic,
list);
196 final Iterator<BluetoothGattCharacteristic> keys = notificationStore.keySet().
iterator();
197 while (keys.hasNext()) {
198 final BluetoothGattCharacteristic characteristic = keys.next();
199 final List<Entry> entries = notificationStore.get(characteristic);
203 ListIterator<Entry> charConfig = entries.listIterator();
204 while (charConfig.hasNext()) {
205 Entry e = charConfig.next();
206 if (e.device.equals(
device))
207 e.isConnected = isConnected;
216 ArrayList<BluetoothDevice>
result =
new ArrayList<BluetoothDevice>();
217 if (!notificationStore.containsKey(characteristic))
220 final ListIterator<Entry>
iter = notificationStore.get(characteristic).listIterator();
221 while (
iter.hasNext())
231 if (!notificationStore.containsKey(characteristic))
234 List<Entry> entries = notificationStore.get(characteristic);
235 for (
int i = 0;
i < entries.size();
i++) {
236 final Entry
entry = entries.get(i);
238 return entries.get(i).value;
245 private static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_UUID = UUID
246 .fromString(
"00002902-0000-1000-8000-00805f9b34fb");
247 ClientCharacteristicManager clientCharacteristicManager =
new ClientCharacteristicManager();
252 if (qtContext ==
null) {
253 Log.w(TAG,
"Missing context object. Peripheral role disabled.");
258 (BluetoothManager) qtContext.getSystemService(
Context.BLUETOOTH_SERVICE);
259 if (mBluetoothManager ==
null) {
260 Log.w(TAG,
"Bluetooth service not available. Peripheral role disabled.");
264 mBluetoothAdapter = mBluetoothManager.getAdapter();
265 if (mBluetoothAdapter ==
null) {
266 Log.w(TAG,
"Missing Bluetooth adapter. Peripheral role disabled.");
270 Log.w(TAG,
"Let's do BTLE Peripheral.");
289 if (mGattServer ==
null) {
290 Log.w(TAG,
"Ignoring connection state event, server is disconnected");
299 mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
301 + status +
", connected devices: " + connectedDevices);
304 int qtControllerState = connectedDevices.
size() > 0 ? 2 : 0;
307 case BluetoothProfile.STATE_CONNECTED:
308 clientCharacteristicManager.markDeviceConnectivity(
device,
true);
309 mRemoteName =
device.getName();
310 mRemoteAddress =
device.getAddress();
312 case BluetoothProfile.STATE_DISCONNECTED:
313 clientCharacteristicManager.markDeviceConnectivity(
device,
false);
314 clearPendingPreparedWrites(
device);
316 if (
device.getAddress().equals(mRemoteAddress)
317 && !connectedDevices.isEmpty()) {
318 mRemoteName = connectedDevices.get(0).getName();
319 mRemoteAddress = connectedDevices.get(0).getAddress();
324 Log.w(TAG,
"Unhandled connection state change: " +
newState);
329 if (qtControllerState == 0) {
330 mPendingServiceAdditions.clear();
335 mSupportedMtu = DEFAULT_LE_ATT_MTU;
340 case BluetoothGatt.GATT_SUCCESS:
344 Log.w(TAG,
"Unhandled error code on peripheral connectionStateChanged: "
346 qtErrorCode = status;
350 leConnectionStateChange(qtObject, qtErrorCode, qtControllerState);
353 synchronized void handleOnServiceAdded(
int status, BluetoothGattService service)
355 if (mGattServer ==
null) {
356 Log.w(TAG,
"Ignoring service addition event, server is disconnected");
360 Log.d(TAG,
"Service " +
service.getUuid().toString() +
" addition result: " + status);
363 ListIterator<BluetoothGattService>
iterator = mPendingServiceAdditions.listIterator();
372 iterator = mPendingServiceAdditions.listIterator();
374 BluetoothGattService nextService =
iterator.next();
375 if (mGattServer.addService(nextService)) {
378 Log.w(TAG,
"Adding service " + nextService.getUuid().toString() +
" failed");
386 BluetoothGattCharacteristic characteristic)
388 if (mGattServer ==
null) {
389 Log.w(TAG,
"Ignoring characteristic read, server is disconnected");
393 byte[] characteristicData =
394 ((QtBluetoothGattCharacteristic)characteristic).getLocalValue();
397 byte[] dataArray = Arrays.copyOfRange(characteristicData,
398 offset, characteristicData.length);
399 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS,
401 }
catch (Exception ex) {
402 Log.w(TAG,
"onCharacteristicReadRequest: " +
requestId +
" "
403 +
offset +
" " + characteristicData.length);
404 ex.printStackTrace();
405 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_FAILURE,
412 BluetoothGattCharacteristic characteristic,
413 boolean preparedWrite,
boolean responseNeeded,
416 if (mGattServer ==
null) {
417 Log.w(TAG,
"Ignoring characteristic write, server is disconnected");
420 Log.w(TAG,
"onCharacteristicWriteRequest " + preparedWrite +
" " +
offset +
" "
422 final int minValueLen = ((QtBluetoothGattCharacteristic)characteristic).minValueLength;
423 final int maxValueLen = ((QtBluetoothGattCharacteristic)characteristic).maxValueLength;
425 int resultStatus = BluetoothGatt.GATT_SUCCESS;
426 boolean sendNotificationOrIndication =
false;
428 if (!preparedWrite) {
432 if (
value.length < minValueLen ||
value.length > maxValueLen) {
434 Log.w(TAG,
"onCharacteristicWriteRequest invalid char value length: "
435 +
value.length +
", min: " + minValueLen +
", max: " + maxValueLen);
436 resultStatus = BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
438 ((QtBluetoothGattCharacteristic)characteristic).setLocalValue(
value);
439 leServerCharacteristicChanged(qtObject, characteristic,
value);
440 sendNotificationOrIndication =
true;
443 Log.w(TAG,
"onCharacteristicWriteRequest: !preparedWrite, offset "
444 +
offset +
", Not supported");
445 resultStatus = BluetoothGatt.GATT_INVALID_OFFSET;
458 if (sendNotificationOrIndication)
459 sendNotificationsOrIndications(characteristic);
463 int offset, BluetoothGattDescriptor descriptor)
465 if (mGattServer ==
null) {
466 Log.w(TAG,
"Ignoring descriptor read, server is disconnected");
470 byte[] dataArray = ((QtBluetoothGattDescriptor)descriptor).getLocalValue();
473 if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
474 dataArray = clientCharacteristicManager.valueFor(
475 descriptor.getCharacteristic(),
device);
476 if (dataArray ==
null)
477 dataArray = ((QtBluetoothGattDescriptor)descriptor).getLocalValue();
480 dataArray = Arrays.copyOfRange(dataArray,
offset, dataArray.length);
481 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS,
483 }
catch (Exception ex) {
484 Log.w(TAG,
"onDescriptorReadRequest: " +
requestId +
" "
485 +
offset +
" " + dataArray.length);
486 ex.printStackTrace();
487 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_FAILURE,
493 BluetoothGattDescriptor descriptor,
boolean preparedWrite,
496 if (mGattServer ==
null) {
497 Log.w(TAG,
"Ignoring descriptor write, server is disconnected");
501 Log.w(TAG,
"onDescriptorWriteRequest " + preparedWrite +
" " +
offset +
" " +
value.length);
502 int resultStatus = BluetoothGatt.GATT_SUCCESS;
504 if (!preparedWrite) {
506 if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
514 if (
value[0] == 0x03) {
515 Log.w(TAG,
"Warning: In CCC of characteristic: "
516 + descriptor.getCharacteristic().getUuid()
517 +
" enabling both NTF & IND requested, enabling NTF only.");
518 value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
520 clientCharacteristicManager.insertOrUpdate(
521 descriptor.getCharacteristic(),
524 ((QtBluetoothGattDescriptor)descriptor).setLocalValue(
value);
525 leServerDescriptorWritten(qtObject, descriptor,
value);
528 Log.w(TAG,
"onDescriptorWriteRequest: !preparedWrite, offset "
529 +
offset +
", Not supported");
530 resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED;
548 if (mGattServer ==
null) {
549 Log.w(TAG,
"Ignoring execute write, server is disconnected");
558 for (WriteEntry
entry : mPendingPreparedWrites) {
562 byte[] newValue =
null;
564 byte[] currentValue = (entry.target instanceof BluetoothGattCharacteristic)
565 ? ((QtBluetoothGattCharacteristic)
entry.target).getLocalValue()
566 : ((QtBluetoothGattDescriptor)
entry.target).getLocalValue();
572 if (
write.second.intValue() > currentValue.length) {
573 clearPendingPreparedWrites(
device);
576 BluetoothGatt.GATT_INVALID_OFFSET,
586 if (
entry.target instanceof QtBluetoothGattCharacteristic &&
587 (
write.second.intValue() +
write.first.length >
588 ((QtBluetoothGattCharacteristic)
entry.target).maxValueLength)) {
589 clearPendingPreparedWrites(
device);
592 BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH,
599 newValue =
new byte[Math.max(
write.second.intValue() +
600 write.first.length, currentValue.length)];
603 System.arraycopy(currentValue, 0, newValue, 0, currentValue.length);
608 currentValue = newValue;
612 if (
entry.target instanceof BluetoothGattCharacteristic) {
613 ((QtBluetoothGattCharacteristic)
entry.target).setLocalValue(newValue);
614 leServerCharacteristicChanged(
615 qtObject, (BluetoothGattCharacteristic)
entry.target, newValue);
617 ((QtBluetoothGattDescriptor)
entry.target).setLocalValue(newValue);
618 leServerDescriptorWritten(
619 qtObject, (BluetoothGattDescriptor)
entry.target, newValue);
625 clearPendingPreparedWrites(
device);
626 mGattServer.sendResponse(
device,
requestId, BluetoothGatt.GATT_SUCCESS, 0,
null);
631 if (mSupportedMtu == mtu)
634 leMtuChanged(qtObject, mSupportedMtu);
640 private BluetoothGattServerCallback mGattServerListener =
new BluetoothGattServerCallback()
649 public void onServiceAdded(
int status, BluetoothGattService service) {
650 super.onServiceAdded(status, service);
651 handleOnServiceAdded(status, service);
663 boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[]
value)
680 boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[]
value)
682 super.onDescriptorWriteRequest(
device,
requestId, descriptor, preparedWrite,
684 handleOnDescriptorWriteRequest(
device,
requestId, descriptor, preparedWrite,
697 super.onNotificationSent(
device, status);
698 Log.w(TAG,
"onNotificationSent" +
device +
" " + status);
703 handleOnMtuChanged(
device, mtu);
708 synchronized int mtu() {
709 return mSupportedMtu;
713 synchronized boolean connectServer()
715 if (mGattServer !=
null)
718 BluetoothManager
manager = (BluetoothManager) qtContext.getSystemService(
Context.BLUETOOTH_SERVICE);
720 Log.w(TAG,
"Bluetooth service not available.");
724 mGattServer =
manager.openGattServer(qtContext, mGattServerListener);
726 return (mGattServer !=
null);
730 synchronized void disconnectServer()
732 if (mGattServer ==
null)
735 clearPendingPreparedWrites(
null);
736 mPendingServiceAdditions.clear();
740 mRemoteName = mRemoteAddress =
"";
741 leConnectionStateChange(qtObject, 0 ,
746 boolean isLeExtendedAdvertisingSupported()
748 if (mBluetoothAdapter ==
null || !mBluetoothAdapter.isEnabled())
750 return mBluetoothAdapter.isLeExtendedAdvertisingSupported();
754 boolean startAdvertising(AdvertiseData advertiseData,
755 AdvertiseData scanResponse,
759 if (mBluetoothAdapter ==
null || !mBluetoothAdapter.isEnabled()) {
760 Log.w(TAG,
"StartAdvertising: Bluetooth not available or offline");
765 if (mLeAdvertiser ==
null && mBluetoothAdapter.isMultipleAdvertisementSupported())
766 mLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
768 if (mLeAdvertiser ==
null) {
769 Log.w(TAG,
"StartAdvertising: LE advertisement not supported");
773 if (!connectServer()) {
774 Log.w(TAG,
"Server::startAdvertising: Cannot open GATT server");
779 mLeAdvertiser.startAdvertisingSet(
settings, advertiseData, scanResponse,
780 null,
null, mAdvertiseSetListener);
781 }
catch (IllegalArgumentException e) {
782 Log.w(TAG,
"Server::startAdvertising: Illegal parameters");
786 Log.w(TAG,
"Starting to advertise.");
791 void stopAdvertising()
793 if (mLeAdvertiser ==
null)
796 mLeAdvertiser.stopAdvertisingSet(mAdvertiseSetListener);
797 Log.w(TAG,
"Advertisement stopped.");
801 synchronized void addService(BluetoothGattService service)
803 if (!connectServer()) {
804 Log.w(TAG,
"Server::addService: Cannot open GATT server");
812 if (mPendingServiceAdditions.isEmpty()) {
813 if (mGattServer.addService(service))
814 mPendingServiceAdditions.add(service);
816 Log.w(TAG,
"Adding service " +
service.getUuid().toString() +
" failed.");
818 mPendingServiceAdditions.add(service);
823 @SuppressWarnings(
"deprecation")
825 BluetoothGattCharacteristic characteristic,
828 mGattServer.notifyCharacteristicChanged(
device, characteristic, confirm);
837 private void sendNotificationsOrIndications(BluetoothGattCharacteristic characteristic)
839 final ListIterator<BluetoothDevice>
iter =
840 clientCharacteristicManager.getToBeUpdatedDevices(characteristic).listIterator();
846 while (
iter.hasNext()) {
848 final byte[] clientCharacteristicConfig =
849 clientCharacteristicManager.valueFor(characteristic,
device);
850 if (clientCharacteristicConfig !=
null) {
851 if (Arrays.equals(clientCharacteristicConfig,
852 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
853 if (Build.VERSION.SDK_INT >= 33) {
854 mGattServer.notifyCharacteristicChanged(
device, characteristic,
false,
855 ((QtBluetoothGattCharacteristic)characteristic).getLocalValue());
857 notifyCharacteristicChange(
device, characteristic,
false);
859 }
else if (Arrays.equals(clientCharacteristicConfig,
860 BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
861 if (Build.VERSION.SDK_INT >= 33) {
862 mGattServer.notifyCharacteristicChanged(
device, characteristic,
true,
863 ((QtBluetoothGattCharacteristic)characteristic).getLocalValue());
865 notifyCharacteristicChange(
device, characteristic,
true);
879 boolean writeCharacteristic(BluetoothGattService service, UUID charUuid,
byte[] newValue)
881 BluetoothGattCharacteristic foundChar =
null;
883 for (BluetoothGattCharacteristic
iter: charList) {
884 if (
iter.getUuid().equals(charUuid) && foundChar ==
null) {
887 }
else if (
iter.getUuid().equals(charUuid)) {
888 Log.w(TAG,
"Found second char with same UUID. Wrong char may have been selected.");
893 if (foundChar ==
null) {
894 Log.w(TAG,
"writeCharacteristic: update for unknown characteristic failed");
901 final int minValueLength = ((QtBluetoothGattCharacteristic)foundChar).minValueLength;
902 final int maxValueLength = ((QtBluetoothGattCharacteristic)foundChar).maxValueLength;
903 if (newValue.length < minValueLength || newValue.length > maxValueLength) {
904 Log.w(TAG,
"writeCharacteristic: invalid value length: "
905 + newValue.length +
", min: " + minValueLength +
", max: " + maxValueLength);
911 ((QtBluetoothGattCharacteristic)foundChar).setLocalValue(newValue);
913 if (mGattServer !=
null)
914 sendNotificationsOrIndications(foundChar);
925 boolean writeDescriptor(BluetoothGattService service, UUID charUuid, UUID descUuid,
928 BluetoothGattDescriptor foundDesc =
null;
929 BluetoothGattCharacteristic foundChar =
null;
931 for (BluetoothGattCharacteristic
iter: charList) {
932 if (!
iter.getUuid().equals(charUuid))
935 if (foundChar ==
null) {
938 Log.w(TAG,
"Found second char with same UUID. Wrong char may have been selected.");
943 if (foundChar !=
null)
944 foundDesc = foundChar.getDescriptor(descUuid);
946 if (foundChar ==
null || foundDesc ==
null) {
947 Log.w(TAG,
"writeDescriptor: update for unknown char or desc failed (" + foundChar +
")");
955 ((QtBluetoothGattDescriptor)foundDesc).setLocalValue(newValue);
964 private AdvertisingSetCallback mAdvertiseSetListener =
new AdvertisingSetCallback()
967 public void onAdvertisingSetStarted(AdvertisingSet advSet,
int txPower,
int status)
969 super.onAdvertisingSetStarted(advSet, txPower, status);
974 case AdvertisingSetCallback.ADVERTISE_SUCCESS:
975 case AdvertisingSetCallback.ADVERTISE_FAILED_ALREADY_STARTED:
977 case AdvertisingSetCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
978 Log.e(TAG,
"Please reduce the size of the advertising data.");
981 case AdvertisingSetCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
985 case AdvertisingSetCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
988 case AdvertisingSetCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
994 leServerAdvertisementError(qtObject, qtErrorCode);
998 public void onAdvertisingSetStopped(AdvertisingSet advSet)
1001 super.onAdvertisingSetStopped(advSet);
1005 native
void leConnectionStateChange(
long qtObject,
int errorCode,
int newState);
1006 native
void leMtuChanged(
long qtObject,
int mtu);
1007 native
void leServerAdvertisementError(
long qtObject,
int status);
1008 native
void leServerCharacteristicChanged(
long qtObject,
1009 BluetoothGattCharacteristic characteristic,
1011 native
void leServerDescriptorWritten(
long qtObject,
1012 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")