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.AdvertiseData.Builder;
20import android.bluetooth.le.AdvertiseSettings;
21import android.bluetooth.le.BluetoothLeAdvertiser;
22import android.os.ParcelUuid;
23import android.os.Build;
24import android.util.Log;
25import android.util.Pair;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Iterator;
30import java.util.LinkedList;
32import java.util.ListIterator;
33import java.util.HashMap;
37 private static final String TAG =
"QtBluetoothGattServer";
40 @SuppressWarnings({
"CanBeFinal",
"WeakerAccess"})
42 @SuppressWarnings(
"WeakerAccess")
44 private Context qtContext =
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>>();
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 {
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();
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();
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) {
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) {
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)
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,
IOBluetoothDevice * device
synchronized String remoteName()
synchronized void disconnectServer()
native void leServerAdvertisementError(long qtObject, int status)
synchronized void handleOnCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic)
boolean writeCharacteristic(BluetoothGattService service, UUID charUuid, byte[] newValue)
native void leConnectionStateChange(long qtObject, int errorCode, int newState)
synchronized void handleOnConnectionStateChange(BluetoothDevice device, int status, int newState)
native void leServerDescriptorWritten(long qtObject, BluetoothGattDescriptor descriptor, byte[] newValue)
synchronized void addService(BluetoothGattService service)
synchronized void handleOnDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)
QtBluetoothLEServer(Context context)
synchronized boolean connectServer()
boolean startAdvertising(AdvertiseData advertiseData, AdvertiseData scanResponse, AdvertiseSettings settings)
synchronized void handleOnDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor)
synchronized void handleOnExecuteWrite(BluetoothDevice device, int requestId, boolean execute)
native void leMtuChanged(long qtObject, int mtu)
synchronized void handleOnMtuChanged(BluetoothDevice device, int mtu)
synchronized void handleOnServiceAdded(int status, BluetoothGattService service)
boolean writeDescriptor(BluetoothGattService service, UUID charUuid, UUID descUuid, byte[] newValue)
native void leServerCharacteristicChanged(long qtObject, BluetoothGattCharacteristic characteristic, byte[] newValue)
synchronized void handleOnCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)
synchronized String remoteAddress()
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
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
[5]
QNearFieldTarget::RequestId requestId
GLenum GLuint GLintptr offset
gzip write("uncompressed data")
QSettings settings("MySoft", "Star Runner")
[0]
QNetworkAccessManager manager