Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlowenergycontroller_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7#include <QCoreApplication>
8#include <QtCore/QLoggingCategory>
9#include <QtCore/QJniEnvironment>
10#include <QtCore/QJniObject>
11#include <QtBluetooth/QLowEnergyServiceData>
12#include <QtBluetooth/QLowEnergyCharacteristicData>
13#include <QtBluetooth/QLowEnergyDescriptorData>
14#include <QtBluetooth/QLowEnergyAdvertisingData>
15#include <QtBluetooth/QLowEnergyAdvertisingParameters>
16#include <QtBluetooth/QLowEnergyConnectionParameters>
17
19
20Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
21
22// BT Core v5.3, 3.2.9, Vol 3, Part F
24
25// Conversion: QBluetoothUuid -> java.util.UUID
27{
28 // cut off leading and trailing brackets
29 const QString output = uuid.toString(QUuid::WithoutBraces);
30
31 QJniObject javaString = QJniObject::fromString(output);
32 QJniObject javaUuid = QJniObject::callStaticMethod<QtJniTypes::UUID>(
33 QtJniTypes::Traits<QtJniTypes::UUID>::className(), "fromString",
34 javaString.object<jstring>());
35
36 return javaUuid;
37}
38
45
47{
49 if (hub)
50 hub->javaObject().callMethod<void>("disconnectServer");
51 }
52}
53
55{
56 const bool isPeripheral = (role == QLowEnergyController::PeripheralRole);
57
58 if (isPeripheral) {
59 qRegisterMetaType<QJniObject>();
60 hub = new LowEnergyNotificationHub(remoteDevice, isPeripheral, this);
61 // we only connect to the peripheral role specific signals
62 // TODO add connections as they get added later on
64 this, &QLowEnergyControllerPrivateAndroid::connectionUpdated);
66 this, &QLowEnergyControllerPrivateAndroid::mtuChanged);
68 this, &QLowEnergyControllerPrivateAndroid::advertisementError);
70 this, &QLowEnergyControllerPrivateAndroid::serverCharacteristicChanged);
72 this, &QLowEnergyControllerPrivateAndroid::serverDescriptorWritten);
73 } else {
74 hub = new LowEnergyNotificationHub(remoteDevice, isPeripheral, this);
75 // we only connect to the central role specific signals
77 this, &QLowEnergyControllerPrivateAndroid::connectionUpdated);
79 this, &QLowEnergyControllerPrivateAndroid::mtuChanged);
81 this, &QLowEnergyControllerPrivateAndroid::servicesDiscovered);
83 this, &QLowEnergyControllerPrivateAndroid::serviceDetailsDiscoveryFinished);
85 this, &QLowEnergyControllerPrivateAndroid::characteristicRead);
87 this, &QLowEnergyControllerPrivateAndroid::descriptorRead);
89 this, &QLowEnergyControllerPrivateAndroid::characteristicWritten);
91 this, &QLowEnergyControllerPrivateAndroid::descriptorWritten);
93 this, &QLowEnergyControllerPrivateAndroid::characteristicChanged);
95 this, &QLowEnergyControllerPrivateAndroid::serviceError);
97 this, &QLowEnergyControllerPrivateAndroid::remoteRssiRead);
98 }
99}
100
102{
103 if (!hub) {
104 qCCritical(QT_BT_ANDROID) << "connectToDevice() LE controller has not been initialized";
105 return;
106 }
107
109 // This is unlikely to happen as a valid local adapter is a precondition
111 qCWarning(QT_BT_ANDROID) << "connectToDevice() failed due to missing permissions";
112 return;
113 }
114
115 // required to pass unit test on default backend
116 if (remoteDevice.isNull()) {
117 qCWarning(QT_BT_ANDROID) << "Invalid/null remote device address";
119 return;
120 }
121
123
124 if (!hub->javaObject().isValid()) {
125 qCWarning(QT_BT_ANDROID) << "Cannot initiate QtBluetoothLE";
128 return;
129 }
130
131 bool result = hub->javaObject().callMethod<jboolean>("connect");
132 if (!result) {
135 return;
136 }
137}
138
140{
141 /* Catch an Android timeout bug. If the device is connecting but cannot
142 * physically connect it seems to ignore the disconnect call below.
143 * At least BluetoothGattCallback.onConnectionStateChange never
144 * arrives. The next BluetoothGatt.connect() works just fine though.
145 * */
146
149
150 if (hub) {
152 hub->javaObject().callMethod<void>("disconnectServer");
153 else
154 hub->javaObject().callMethod<void>("disconnect");
155 }
156
159}
160
162{
163 // No need to check bluetooth permissions here as 'connected' is a precondition
164
165 if (hub && hub->javaObject().callMethod<jboolean>("discoverServices")) {
166 qCDebug(QT_BT_ANDROID) << "Service discovery initiated";
167 } else {
168 //revert to connected state
171 }
172}
173
176{
177 Q_UNUSED(mode);
178 if (!serviceList.contains(service)) {
179 qCWarning(QT_BT_ANDROID) << "Discovery of unknown service" << service.toString()
180 << "not possible";
181 return;
182 }
183
184 if (!hub)
185 return;
186
187 QString tempUuid = service.toString(QUuid::WithoutBraces);
188
189 QJniEnvironment env;
190 QJniObject uuid = QJniObject::fromString(tempUuid);
191 bool readAllValues = mode == QLowEnergyService::FullDiscovery;
192 bool result = hub->javaObject().callMethod<jboolean>("discoverServiceDetails",
193 uuid.object<jstring>(),
194 readAllValues);
195 if (!result) {
196 QSharedPointer<QLowEnergyServicePrivate> servicePrivate =
197 serviceList.value(service);
198 if (!servicePrivate.isNull()) {
199 servicePrivate->setError(QLowEnergyService::UnknownError);
200 servicePrivate->setState(QLowEnergyService::RemoteService);
201 }
202 qCWarning(QT_BT_ANDROID) << "Cannot discover details for" << service.toString();
203 return;
204 }
205
206 qCDebug(QT_BT_ANDROID) << "Discovery of" << service << "started";
207}
208
210 const QSharedPointer<QLowEnergyServicePrivate> service,
211 const QLowEnergyHandle charHandle,
212 const QByteArray &newValue,
214{
215 //TODO don't ignore WriteWithResponse, right now we assume responses
216 Q_ASSERT(!service.isNull());
217
218 if (!service->characteristicList.contains(charHandle))
219 return;
220
221 QJniEnvironment env;
222 jbyteArray payload;
223 payload = env->NewByteArray(newValue.size());
224 env->SetByteArrayRegion(payload, 0, newValue.size(),
225 (jbyte *)newValue.constData());
226
227 bool result = false;
228 if (hub) {
230 qCDebug(QT_BT_ANDROID) << "Write characteristic with handle " << charHandle
231 << newValue.toHex() << "(service:" << service->uuid
232 << ", writeWithResponse:" << (mode == QLowEnergyService::WriteWithResponse)
233 << ", signed:" << (mode == QLowEnergyService::WriteSigned) << ")";
234 result = hub->javaObject().callMethod<jboolean>("writeCharacteristic",
235 charHandle, payload, jint(mode));
236 } else { // peripheral mode
237 qCDebug(QT_BT_ANDROID) << "Write server characteristic with handle " << charHandle
238 << newValue.toHex() << "(service:" << service->uuid;
239
240 const auto &characteristic = characteristicForHandle(charHandle);
241 if (characteristic.isValid()) {
242 const QJniObject charUuid = javaUuidfromQtUuid(characteristic.uuid());
243 result = hub->javaObject().callMethod<jboolean>(
244 "writeCharacteristic",
245 service->androidService.object<QtJniTypes::BluetoothGattService>(),
246 charUuid.object<QtJniTypes::UUID>(), payload);
247 if (result)
248 service->characteristicList[charHandle].value = newValue;
249 }
250 }
251 }
252
253 env->DeleteLocalRef(payload);
254
255 if (!result)
257}
258
260 const QSharedPointer<QLowEnergyServicePrivate> service,
261 const QLowEnergyHandle charHandle,
262 const QLowEnergyHandle descHandle,
263 const QByteArray &newValue)
264{
265 Q_ASSERT(!service.isNull());
266
267 QJniEnvironment env;
268 jbyteArray payload;
269 payload = env->NewByteArray(newValue.size());
270 env->SetByteArrayRegion(payload, 0, newValue.size(),
271 (jbyte *)newValue.constData());
272
273 bool result = false;
274 if (hub) {
276 qCDebug(QT_BT_ANDROID) << "Write descriptor with handle " << descHandle
277 << newValue.toHex() << "(service:" << service->uuid << ")";
278 result = hub->javaObject().callMethod<jboolean>("writeDescriptor",
279 descHandle, payload);
280 } else {
281 const auto &characteristic = characteristicForHandle(charHandle);
282 const auto &descriptor = descriptorForHandle(descHandle);
283 if (characteristic.isValid() && descriptor.isValid()) {
284 qCDebug(QT_BT_ANDROID) << "Write descriptor" << descriptor.uuid()
285 << "(service:" << service->uuid
286 << "char: " << characteristic.uuid() << ")";
287 const QJniObject charUuid = javaUuidfromQtUuid(characteristic.uuid());
288 const QJniObject descUuid = javaUuidfromQtUuid(descriptor.uuid());
289 result = hub->javaObject().callMethod<jboolean>(
290 "writeDescriptor",
291 service->androidService.object<QtJniTypes::BluetoothGattService>(),
292 charUuid.object<QtJniTypes::UUID>(), descUuid.object<QtJniTypes::UUID>(),
293 payload);
294 if (result)
295 service->characteristicList[charHandle].descriptorList[descHandle].value = newValue;
296 }
297 }
298 }
299
300 env->DeleteLocalRef(payload);
301
302 if (!result)
304}
305
307 const QSharedPointer<QLowEnergyServicePrivate> service,
308 const QLowEnergyHandle charHandle)
309{
310 Q_ASSERT(!service.isNull());
311
312 if (!service->characteristicList.contains(charHandle))
313 return;
314
315 QJniEnvironment env;
316 bool result = false;
317 if (hub) {
318 qCDebug(QT_BT_ANDROID) << "Read characteristic with handle"
319 << charHandle << service->uuid;
320 result = hub->javaObject().callMethod<jboolean>("readCharacteristic", charHandle);
321 }
322
323 if (!result)
325}
326
328 const QSharedPointer<QLowEnergyServicePrivate> service,
329 const QLowEnergyHandle /*charHandle*/,
330 const QLowEnergyHandle descriptorHandle)
331{
332 Q_ASSERT(!service.isNull());
333
334 QJniEnvironment env;
335 bool result = false;
336 if (hub) {
337 qCDebug(QT_BT_ANDROID) << "Read descriptor with handle"
338 << descriptorHandle << service->uuid;
339 result = hub->javaObject().callMethod<jboolean>("readDescriptor", descriptorHandle);
340 }
341
342 if (!result)
344}
345
346void QLowEnergyControllerPrivateAndroid::connectionUpdated(
349{
350 qCDebug(QT_BT_ANDROID) << "Connection updated:"
351 << "error:" << errorCode
352 << "oldState:" << state
353 << "newState:" << newState;
354
356 peripheralConnectionUpdated(newState, errorCode);
357 else
358 centralConnectionUpdated(newState, errorCode);
359}
360
361void QLowEnergyControllerPrivateAndroid::mtuChanged(int mtu)
362{
364 qCDebug(QT_BT_ANDROID) << "MTU updated:"
365 << "mtu:" << mtu;
366 emit q->mtuChanged(mtu);
367}
368
369void QLowEnergyControllerPrivateAndroid::remoteRssiRead(int rssi, bool success)
370{
372 if (success) {
373 // BT Core v5.3, 7.5.4, Vol 4, Part E
374 // The LE RSSI can take values -127..127 => narrowing to qint16 is safe
375 emit q->rssiRead(rssi);
376 } else {
377 qCDebug(QT_BT_ANDROID) << "Reading remote RSSI failed";
379 }
380}
381
382// called if server/peripheral
383void QLowEnergyControllerPrivateAndroid::peripheralConnectionUpdated(
386{
387 // Java errorCode can be larger than max QLowEnergyController::Error
390
391 if (errorCode != QLowEnergyController::NoError)
392 setError(errorCode);
393
396
397 // disconnect implies stop of advertisement
400
401 // The remote name and address may have changed
402 if (hub) {
404 hub->javaObject().callMethod<jstring>("remoteAddress").toString());
405 remoteName = hub->javaObject().callMethod<jstring>("remoteName").toString();
406 }
407
409 // Emit (dis)connected if the connection state changes
412 emit q->disconnected();
415 emit q->connected();
416 }
417}
418
419// called if client/central
420void QLowEnergyControllerPrivateAndroid::centralConnectionUpdated(
423{
425
427
428 if (errorCode != QLowEnergyController::NoError) {
429 // ConnectionError if transition from Connecting to Connected
432 /* There is a bug in Android, when connecting to an unconnectable
433 * device. The connection times out and Android sends error code
434 * 133 (doesn't exist) and STATE_CONNECTED. A subsequent disconnect()
435 * call never sends a STATE_DISCONNECTED either.
436 * As workaround we will trigger disconnect when we encounter
437 * error during connect attempt. This leaves the controller
438 * in a cleaner state.
439 * */
441 }
442 else
443 setError(errorCode);
444 }
445
450
451 // Invalidate the services if the disconnect came from the remote end.
452 // Qtherwise we disconnected via QLowEnergyController::disconnectDevice() which
453 // triggered invalidation already
454 if (!serviceList.isEmpty()) {
457 }
458 emit q->disconnected();
460 && oldState != QLowEnergyController::ConnectedState ) {
461 emit q->connected();
462 }
463}
464
465void QLowEnergyControllerPrivateAndroid::servicesDiscovered(
466 QLowEnergyController::Error errorCode, const QString &foundServices)
467{
469
470 if (errorCode == QLowEnergyController::NoError) {
471 //Android delivers all services in one go
472 const QStringList list = foundServices.split(QChar::Space, Qt::SkipEmptyParts);
473 for (const QString &entry : list) {
475 if (service.isNull())
476 return;
477
479 priv->uuid = service;
480 priv->setController(this);
481
482 QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
483 serviceList.insert(service, pointer);
484
485 emit q->serviceDiscovered(QBluetoothUuid(entry));
486 }
487
489 emit q->discoveryFinished();
490 } else {
491 setError(errorCode);
493 }
494}
495
496void QLowEnergyControllerPrivateAndroid::serviceDetailsDiscoveryFinished(
497 const QString &serviceUuid, int startHandle, int endHandle)
498{
499 const QBluetoothUuid service(serviceUuid);
500 if (!serviceList.contains(service)) {
501 qCWarning(QT_BT_ANDROID) << "Discovery done of unknown service:"
502 << service.toString();
503 return;
504 }
505
506 //update service data
507 QSharedPointer<QLowEnergyServicePrivate> pointer =
508 serviceList.value(service);
509 pointer->startHandle = startHandle;
510 pointer->endHandle = endHandle;
511
512 if (hub && hub->javaObject().isValid()) {
513 QJniObject uuid = QJniObject::fromString(serviceUuid);
514 QJniObject javaIncludes = hub->javaObject().callMethod<jstring>(
515 "includedServices", uuid.object<jstring>());
516 if (javaIncludes.isValid()) {
517 const QStringList list = javaIncludes.toString()
518 .split(QChar::Space, Qt::SkipEmptyParts);
519 for (const QString &entry : list) {
521 if (service.isNull())
522 return;
523
524 pointer->includedServices.append(service);
525
526 // update the type of the included service
527 QSharedPointer<QLowEnergyServicePrivate> otherService =
528 serviceList.value(service);
529 if (!otherService.isNull())
530 otherService->type |= QLowEnergyService::IncludedService;
531 }
532 }
533 }
534
535 qCDebug(QT_BT_ANDROID) << "Service" << serviceUuid << "discovered (start:"
536 << startHandle << "end:" << endHandle << ")" << pointer.data();
537
539}
540
541void QLowEnergyControllerPrivateAndroid::characteristicRead(
542 const QBluetoothUuid &serviceUuid, int handle,
543 const QBluetoothUuid &charUuid, int properties, const QByteArray &data)
544{
545 if (!serviceList.contains(serviceUuid))
546 return;
547
548 QSharedPointer<QLowEnergyServicePrivate> service =
549 serviceList.value(serviceUuid);
550 QLowEnergyHandle charHandle = handle;
551
553 service->characteristicList[charHandle];
554
555 //Android uses same property value as Qt which is the Bluetooth LE standard
556 charDetails.properties = QLowEnergyCharacteristic::PropertyType(properties);
557 charDetails.uuid = charUuid;
558 charDetails.value = data;
559 //value handle always one larger than characteristics value handle
560 charDetails.valueHandle = charHandle + 1;
561
563 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
564 if (!characteristic.isValid()) {
565 qCWarning(QT_BT_ANDROID) << "characteristicRead: Cannot find characteristic";
566 return;
567 }
568 emit service->characteristicRead(characteristic, data);
569 }
570}
571
572void QLowEnergyControllerPrivateAndroid::descriptorRead(
573 const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid,
574 int descHandle, const QBluetoothUuid &descUuid, const QByteArray &data)
575{
576 if (!serviceList.contains(serviceUuid))
577 return;
578
579 QSharedPointer<QLowEnergyServicePrivate> service =
580 serviceList.value(serviceUuid);
581
582 bool entryUpdated = false;
583
584 CharacteristicDataMap::iterator charIt = service->characteristicList.begin();
585 for ( ; charIt != service->characteristicList.end(); ++charIt) {
586 QLowEnergyServicePrivate::CharData &charDetails = charIt.value();
587
588 if (charDetails.uuid != charUuid)
589 continue;
590
591 // new entry created if it doesn't exist
593 charDetails.descriptorList[descHandle];
594 descDetails.uuid = descUuid;
595 descDetails.value = data;
596 entryUpdated = true;
597 break;
598 }
599
600 if (!entryUpdated) {
601 qCWarning(QT_BT_ANDROID) << "Cannot find/update descriptor"
602 << descUuid << charUuid << serviceUuid;
604 QLowEnergyDescriptor descriptor = descriptorForHandle(descHandle);
605 if (!descriptor.isValid()) {
606 qCWarning(QT_BT_ANDROID) << "descriptorRead: Cannot find descriptor";
607 return;
608 }
609 emit service->descriptorRead(descriptor, data);
610 }
611}
612
613void QLowEnergyControllerPrivateAndroid::characteristicWritten(
614 int charHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode)
615{
616 QSharedPointer<QLowEnergyServicePrivate> service =
617 serviceForHandle(charHandle);
618 if (service.isNull())
619 return;
620
621 qCDebug(QT_BT_ANDROID) << "Characteristic write confirmation" << service->uuid
622 << charHandle << data.toHex() << errorCode;
623
624 if (errorCode != QLowEnergyService::NoError) {
625 service->setError(errorCode);
626 return;
627 }
628
629 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
630 if (!characteristic.isValid()) {
631 qCWarning(QT_BT_ANDROID) << "characteristicWritten: Cannot find characteristic";
632 return;
633 }
634
635 // only update cache when property is readable. Otherwise it remains
636 // empty.
637 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
638 updateValueOfCharacteristic(charHandle, data, false);
639 emit service->characteristicWritten(characteristic, data);
640}
641
642void QLowEnergyControllerPrivateAndroid::descriptorWritten(
643 int descHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode)
644{
645 QSharedPointer<QLowEnergyServicePrivate> service =
646 serviceForHandle(descHandle);
647 if (service.isNull())
648 return;
649
650 qCDebug(QT_BT_ANDROID) << "Descriptor write confirmation" << service->uuid
651 << descHandle << data.toHex() << errorCode;
652
653 if (errorCode != QLowEnergyService::NoError) {
654 service->setError(errorCode);
655 return;
656 }
657
658 QLowEnergyDescriptor descriptor = descriptorForHandle(descHandle);
659 if (!descriptor.isValid()) {
660 qCWarning(QT_BT_ANDROID) << "descriptorWritten: Cannot find descriptor";
661 return;
662 }
663
664 updateValueOfDescriptor(descriptor.characteristicHandle(),
665 descHandle, data, false);
666 emit service->descriptorWritten(descriptor, data);
667}
668
669void QLowEnergyControllerPrivateAndroid::serverDescriptorWritten(
670 const QJniObject &jniDesc, const QByteArray &newValue)
671{
672 qCDebug(QT_BT_ANDROID) << "Server descriptor change notification" << newValue.toHex();
673
674 // retrieve service, char and desc uuids
675 const QJniObject jniChar = jniDesc.callMethod<QtJniTypes::BluetoothGattCharacteristic>(
676 "getCharacteristic");
677 if (!jniChar.isValid())
678 return;
679
680 const QJniObject jniService =
681 jniChar.callMethod<QtJniTypes::BluetoothGattService>("getService");
682 if (!jniService.isValid())
683 return;
684
685 QJniObject jniUuid = jniService.callMethod<QtJniTypes::UUID>("getUuid");
686 const QBluetoothUuid serviceUuid(jniUuid.toString());
687 if (serviceUuid.isNull())
688 return;
689
690 // TODO test if two service with same uuid exist
691 if (!localServices.contains(serviceUuid))
692 return;
693
694 jniUuid = jniChar.callMethod<QtJniTypes::UUID>("getUuid");
695 const QBluetoothUuid characteristicUuid(jniUuid.toString());
696 if (characteristicUuid.isNull())
697 return;
698
699 jniUuid = jniDesc.callMethod<QtJniTypes::UUID>("getUuid");
700 const QBluetoothUuid descriptorUuid(jniUuid.toString());
701 if (descriptorUuid.isNull())
702 return;
703
704 // find matching QLEDescriptor
705 auto servicePrivate = localServices.value(serviceUuid);
706 // TODO test if service contains two characteristics with same uuid
707 // or characteristic contains two descriptors with same uuid
708 const auto handleList = servicePrivate->characteristicList.keys();
709 for (const auto charHandle: handleList) {
710 const auto &charData = servicePrivate->characteristicList.value(charHandle);
711 if (charData.uuid != characteristicUuid)
712 continue;
713
714 const auto &descHandleList = charData.descriptorList.keys();
715 for (const auto descHandle: descHandleList) {
716 const auto &descData = charData.descriptorList.value(descHandle);
717 if (descData.uuid != descriptorUuid)
718 continue;
719
720 qCDebug(QT_BT_ANDROID) << "serverDescriptorChanged: Matching descriptor"
721 << descriptorUuid << "in char" << characteristicUuid
722 << "of service" << serviceUuid;
723
724 servicePrivate->characteristicList[charHandle].descriptorList[descHandle].value = newValue;
725
726 emit servicePrivate->descriptorWritten(
727 QLowEnergyDescriptor(servicePrivate, charHandle, descHandle),
728 newValue);
729 return;
730 }
731 }
732}
733
734void QLowEnergyControllerPrivateAndroid::characteristicChanged(
735 int charHandle, const QByteArray &data)
736{
737 QSharedPointer<QLowEnergyServicePrivate> service =
738 serviceForHandle(charHandle);
739 if (service.isNull())
740 return;
741
742 qCDebug(QT_BT_ANDROID) << "Characteristic change notification" << service->uuid
743 << charHandle << data.toHex() << "length:" << data.size();
744
745 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
746 if (!characteristic.isValid()) {
747 qCWarning(QT_BT_ANDROID) << "characteristicChanged: Cannot find characteristic";
748 return;
749 }
750
751 // only update cache when property is readable. Otherwise it remains
752 // empty.
753 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
754 updateValueOfCharacteristic(characteristic.attributeHandle(),
755 data, false);
756 emit service->characteristicChanged(characteristic, data);
757}
758
759void QLowEnergyControllerPrivateAndroid::serverCharacteristicChanged(
760 const QJniObject &characteristic, const QByteArray &newValue)
761{
762 qCDebug(QT_BT_ANDROID) << "Server characteristic change notification"
763 << newValue.toHex() << "length:" << newValue.size();
764
765 // match characteristic to servicePrivate
766 QJniObject service = characteristic.callMethod<QtJniTypes::BluetoothGattService>(
767 "getService");
768 if (!service.isValid())
769 return;
770
771 QJniObject jniUuid = service.callMethod<QtJniTypes::UUID>("getUuid");
772 QBluetoothUuid serviceUuid(jniUuid.toString());
773 if (serviceUuid.isNull())
774 return;
775
776 // TODO test if two service with same uuid exist
777 if (!localServices.contains(serviceUuid))
778 return;
779
780 auto servicePrivate = localServices.value(serviceUuid);
781
782 jniUuid = characteristic.callMethod<QtJniTypes::UUID>("getUuid");
783 QBluetoothUuid characteristicUuid(jniUuid.toString());
784 if (characteristicUuid.isNull())
785 return;
786
787 QLowEnergyHandle foundHandle = 0;
788 const auto handleList = servicePrivate->characteristicList.keys();
789 // TODO test if service contains two characteristics with same uuid
790 for (const auto handle: handleList) {
791 QLowEnergyServicePrivate::CharData &charData = servicePrivate->characteristicList[handle];
792 if (charData.uuid != characteristicUuid)
793 continue;
794
795 qCDebug(QT_BT_ANDROID) << "serverCharacteristicChanged: Matching characteristic"
796 << characteristicUuid << " on " << serviceUuid;
797 charData.value = newValue;
798 foundHandle = handle;
799 break;
800 }
801
802 if (!foundHandle)
803 return;
804
805 emit servicePrivate->characteristicChanged(
806 QLowEnergyCharacteristic(servicePrivate, foundHandle), newValue);
807}
808
809void QLowEnergyControllerPrivateAndroid::serviceError(
810 int attributeHandle, QLowEnergyService::ServiceError errorCode)
811{
812 // ignore call if it isn't really an error
813 if (errorCode == QLowEnergyService::NoError)
814 return;
815
816 QSharedPointer<QLowEnergyServicePrivate> service =
817 serviceForHandle(attributeHandle);
818 Q_ASSERT(!service.isNull());
819
820 // ATM we don't really use attributeHandle but later on we might
821 // want to associate the error code with a char or desc
822 service->setError(errorCode);
823}
824
825void QLowEnergyControllerPrivateAndroid::advertisementError(int errorCode)
826{
828
829 switch (errorCode)
830 {
831 case 1: // AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE
832 errorString = QLowEnergyController::tr("Advertisement data is larger than 31 bytes");
833 break;
834 case 2: // AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED
835 errorString = QLowEnergyController::tr("Advertisement feature not supported on the platform");
836 break;
837 case 3: // AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR
838 errorString = QLowEnergyController::tr("Error occurred trying to start advertising");
839 break;
840 case 4: // AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS
841 errorString = QLowEnergyController::tr("Failed due to too many advertisers");
842 break;
843 default:
844 errorString = QLowEnergyController::tr("Unknown advertisement error");
845 break;
846 }
847
849 emit q->errorOccurred(error);
850
851 // not relevant states in peripheral mode
854
855 switch (state)
856 {
861 // noop as remote is already connected or about to disconnect.
862 // when connection drops we reset to unconnected anyway
863 break;
864
867 break;
868 default:
869 break;
870 }
871}
872
874{
875 QString output = uuid.toString();
876 // cut off leading and trailing brackets
877 output = output.mid(1, output.size()-2);
878
879 QJniObject javaString = QJniObject::fromString(output);
880 QJniObject parcelUuid = QJniObject::callStaticMethod<QtJniTypes::ParcelUuid>(
881 QtJniTypes::Traits<QtJniTypes::ParcelUuid>::className(), "fromString",
882 javaString.object<jstring>());
883
884 return parcelUuid;
885}
886
888{
889 QJniObject builder = QJniObject::construct<QtJniTypes::AdvertiseDataBuilder>();
890
891 // device name cannot be set but there is choice to show it or not
892 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>(
893 "setIncludeDeviceName", !data.localName().isEmpty());
894 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>(
895 "setIncludeTxPowerLevel", data.includePowerLevel());
896 const auto services = data.services();
897 for (const auto &service : services) {
898 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>("addServiceUuid",
899 javaParcelUuidfromQtUuid(service).object<QtJniTypes::ParcelUuid>());
900 }
901
902 if (!data.manufacturerData().isEmpty()) {
903 QJniEnvironment env;
904 const qint32 nativeSize = data.manufacturerData().size();
905 jbyteArray nativeData = env->NewByteArray(nativeSize);
906 env->SetByteArrayRegion(nativeData, 0, nativeSize,
907 reinterpret_cast<const jbyte*>(data.manufacturerData().constData()));
908 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>(
909 "addManufacturerData", jint(data.manufacturerId()), nativeData);
910 env->DeleteLocalRef(nativeData);
911
912 if (!builder.isValid()) {
913 qCWarning(QT_BT_ANDROID) << "Cannot set manufacturer id/data";
914 }
915 }
916
917 /*// TODO Qt vs Java API mismatch
918 -> Qt assumes rawData() is a global field
919 -> Android pairs rawData() per service uuid
920 if (!data.rawData().isEmpty()) {
921 QJniEnvironment env;
922 qint32 nativeSize = data.rawData().size();
923 jbyteArray nativeData = env->NewByteArray(nativeSize);
924 env->SetByteArrayRegion(nativeData, 0, nativeSize,
925 reinterpret_cast<const jbyte*>(data.rawData().constData()));
926 builder = builder.callObjectMethod("addServiceData",
927 "(Landroid/os/ParcelUuid;[B])Landroid/bluetooth/le/AdvertiseData$Builder;",
928 data.rawData().object(), nativeData);
929 env->DeleteLocalRef(nativeData);
930
931 if (env.checkAndClearExceptions()) {
932 qCWarning(QT_BT_ANDROID) << "Cannot set advertisement raw data";
933 }
934 }*/
935
936 QJniObject javaAdvertiseData = builder.callMethod<QtJniTypes::AdvertiseData>("build");
937 return javaAdvertiseData;
938}
939
941{
942 QJniObject builder = QJniObject::construct<QtJniTypes::AdvertiseSettingsBuilder>();
943
944 bool connectable = false;
945 switch (params.mode())
946 {
948 connectable = true;
949 break;
952 connectable = false;
953 break;
954 // intentionally no default case
955 }
956 builder = builder.callMethod<QtJniTypes::AdvertiseSettingsBuilder>(
957 "setConnectable", connectable);
958
959 /* TODO No Android API for further QLowEnergyAdvertisingParameters options
960 * Android TxPowerLevel, AdvertiseMode and Timeout not mappable to Qt
961 */
962
963 QJniObject javaAdvertiseSettings = builder.callMethod<QtJniTypes::AdvertiseSettings>("build");
964 return javaAdvertiseSettings;
965}
966
967
969 const QLowEnergyAdvertisingData &advertisingData,
970 const QLowEnergyAdvertisingData &scanResponseData)
971{
973
975 qCWarning(QT_BT_ANDROID) << "startAdvertising() failed due to missing permissions";
978 return;
979 }
980
981 if (!hub || !hub->javaObject().isValid()) {
982 qCWarning(QT_BT_ANDROID) << "Cannot initiate QtBluetoothLEServer";
985 return;
986 }
987
988 // Pass on advertisingData, scanResponse & AdvertiseSettings
989 QJniObject jAdvertiseData = createJavaAdvertiseData(advertisingData);
990 QJniObject jScanResponse = createJavaAdvertiseData(scanResponseData);
991 QJniObject jAdvertiseSettings = createJavaAdvertiseSettings(params);
992
993 const bool result = hub->javaObject().callMethod<jboolean>("startAdvertising",
994 jAdvertiseData.object<QtJniTypes::AdvertiseData>(),
995 jScanResponse.object<QtJniTypes::AdvertiseData>(),
996 jAdvertiseSettings.object<QtJniTypes::AdvertiseSettings>());
997 if (!result) {
1000 }
1001}
1002
1004{
1006 hub->javaObject().callMethod<void>("stopAdvertising");
1007}
1008
1010{
1011 // Android does not permit specification of specific latency or min/max
1012 // connection intervals (see BluetoothGatt.requestConnectionPriority()
1013 // In fact, each device manufacturer is permitted to change those values via a config
1014 // file too. Therefore we can only make an approximated guess (see implementation below)
1015 // In addition there is no feedback signal (known bug) from the hardware layer as per v24.
1016
1017 // TODO recheck in later Android releases whether callback for
1018 // BluetoothGatt.requestConnectionPriority() was added
1019
1021 qCWarning(QT_BT_ANDROID) << "On Android, connection requests only work for central role";
1022 return;
1023 }
1024
1025 const bool result = hub->javaObject().callMethod<jboolean>("requestConnectionUpdatePriority",
1026 params.minimumInterval());
1027 if (!result)
1028 qCWarning(QT_BT_ANDROID) << "Cannot set connection update priority";
1029}
1030
1031/*
1032 * Returns the Java char permissions based on the given characteristic data.
1033 */
1035{
1036 int permission = 0;
1037 if (charData.properties() & QLowEnergyCharacteristic::Read) {
1038 if (int(charData.readConstraints()) == 0 // nothing is equivalent to simple read
1039 || (charData.readConstraints()
1041 permission |= QJniObject::getStaticField<jint>(
1042 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1043 "PERMISSION_READ");
1044 }
1045
1046 if (charData.readConstraints()
1048 permission |= QJniObject::getStaticField<jint>(
1049 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1050 "PERMISSION_READ_ENCRYPTED");
1051 }
1052
1053 if (charData.readConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1054 permission |= QJniObject::getStaticField<jint>(
1055 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1056 "PERMISSION_READ_ENCRYPTED_MITM");
1057 }
1058 }
1059
1060 if (charData.properties() &
1062 if (int(charData.writeConstraints()) == 0 // no flag is equivalent ti simple write
1063 || (charData.writeConstraints()
1065 permission |= QJniObject::getStaticField<jint>(
1066 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1067 "PERMISSION_WRITE");
1068 }
1069
1070 if (charData.writeConstraints()
1072 permission |= QJniObject::getStaticField<jint>(
1073 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1074 "PERMISSION_WRITE_ENCRYPTED");
1075 }
1076
1077 if (charData.writeConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1078 permission |= QJniObject::getStaticField<jint>(
1079 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1080 "PERMISSION_WRITE_ENCRYPTED_MITM");
1081 }
1082 }
1083
1084 if (charData.properties() & QLowEnergyCharacteristic::WriteSigned) {
1085 if (charData.writeConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1086 permission |= QJniObject::getStaticField<jint>(
1087 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1088 "PERMISSION_WRITE_SIGNED_MITM");
1089 } else {
1090 permission |= QJniObject::getStaticField<jint>(
1091 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1092 "PERMISSION_WRITE_SIGNED");
1093 }
1094 }
1095
1096 return permission;
1097}
1098
1099/*
1100 * Returns the Java desc permissions based on the given descriptor data.
1101 */
1103{
1104 int permissions = 0;
1105
1106 if (descData.isReadable()) {
1107 if (int(descData.readConstraints()) == 0 // empty is equivalent to simple read
1108 || (descData.readConstraints()
1110 permissions |= QJniObject::getStaticField<jint>(
1111 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1112 "PERMISSION_READ");
1113 }
1114
1115 if (descData.readConstraints()
1117 permissions |= QJniObject::getStaticField<jint>(
1118 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1119 "PERMISSION_READ_ENCRYPTED");
1120 }
1121
1123 permissions |= QJniObject::getStaticField<jint>(
1124 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1125 "PERMISSION_READ_ENCRYPTED_MITM");
1126 }
1127 }
1128
1129 if (descData.isWritable()) {
1130 if (int(descData.readConstraints()) == 0 // empty is equivalent to simple read
1131 || (descData.readConstraints()
1133 permissions |= QJniObject::getStaticField<jint>(
1134 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1135 "PERMISSION_WRITE");
1136 }
1137
1138 if (descData.readConstraints()
1140 permissions |= QJniObject::getStaticField<jint>(
1141 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1142 "PERMISSION_WRITE_ENCRYPTED");
1143 }
1144
1146 permissions |= QJniObject::getStaticField<jint>(
1147 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1148 "PERMISSION_WRITE_ENCRYPTED_MITM");
1149 }
1150 }
1151
1152 return permissions;
1153}
1154
1156 QLowEnergyHandle startHandle)
1157{
1158 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(startHandle);
1159 if (service.isNull())
1160 return;
1161
1162 // create BluetoothGattService object
1163 jint sType = QJniObject::getStaticField<jint>(
1164 QtJniTypes::Traits<QtJniTypes::BluetoothGattService>::className(),
1165 "SERVICE_TYPE_PRIMARY");
1167 sType = QJniObject::getStaticField<jint>(
1168 QtJniTypes::Traits<QtJniTypes::BluetoothGattService>::className(),
1169 "SERVICE_TYPE_SECONDARY");
1170
1171 service->androidService = QJniObject::construct<QtJniTypes::BluetoothGattService>(
1172 javaUuidfromQtUuid(service->uuid).object<QtJniTypes::UUID>(), sType);
1173
1174 // add included services, which must have been added earlier already
1175 const QList<QLowEnergyService*> includedServices = serviceData.includedServices();
1176 for (const auto includedServiceEntry: includedServices) {
1177 //TODO test this end-to-end
1178 const jboolean result = service->androidService.callMethod<jboolean>("addService",
1179 includedServiceEntry->d_ptr->androidService.object<QtJniTypes::BluetoothGattService>());
1180 if (!result)
1181 qWarning(QT_BT_ANDROID) << "Cannot add included service " << includedServiceEntry->serviceUuid()
1182 << "to current service" << service->uuid;
1183 }
1184
1185 // add characteristics
1186 const QList<QLowEnergyCharacteristicData> serviceCharsData = serviceData.characteristics();
1187 for (const auto &charData: serviceCharsData) {
1188
1189 // User may have set minimum and/or maximum characteristic value size. Enforce
1190 // them here. If the user has not defined these limits, the default values 0..INT_MAX
1191 // do not limit anything. If the sizes are violated here (ie. when adding the
1192 // characteristics), regard it as a programming error.
1193 if (charData.value().size() < charData.minimumValueLength() ||
1194 charData.value().size() > charData.maximumValueLength()) {
1195 qWarning() << "Warning: Ignoring characteristic" << charData.uuid()
1196 << "with invalid length:" << charData.value().size()
1197 << "(minimum:" << charData.minimumValueLength()
1198 << "maximum:" << charData.maximumValueLength() << ").";
1199 continue;
1200 }
1201
1202 // Issue a warning if the attribute length exceeds the bluetooth standard limit.
1203 if (charData.value().size() > BTLE_MAX_ATTRIBUTE_VALUE_SIZE) {
1204 qCWarning(QT_BT_ANDROID) << "Warning: characteristic" << charData.uuid() << "size"
1205 << "exceeds the standard: " << BTLE_MAX_ATTRIBUTE_VALUE_SIZE
1206 << ", value size:" << charData.value().size();
1207 }
1208
1209 QJniObject javaChar = QJniObject::construct<QtJniTypes::QtBtGattCharacteristic>(
1210 javaUuidfromQtUuid(charData.uuid()).object<QtJniTypes::UUID>(),
1211 int(charData.properties()),
1212 setupCharPermissions(charData),
1213 charData.minimumValueLength(),
1214 charData.maximumValueLength());
1215
1216 QJniEnvironment env;
1217 jbyteArray jb = env->NewByteArray(charData.value().size());
1218 env->SetByteArrayRegion(jb, 0, charData.value().size(), (jbyte*)charData.value().data());
1219 jboolean success = javaChar.callMethod<jboolean>("setLocalValue", jb);
1220 if (!success)
1221 qCWarning(QT_BT_ANDROID) << "Cannot setup initial characteristic value for " << charData.uuid();
1222 env->DeleteLocalRef(jb);
1223
1224 const QList<QLowEnergyDescriptorData> descriptorList = charData.descriptors();
1225 for (const auto &descData: descriptorList) {
1226 QJniObject javaDesc = QJniObject::construct<QtJniTypes::QtBtGattDescriptor>(
1227 javaUuidfromQtUuid(descData.uuid()).object<QtJniTypes::UUID>(),
1228 setupDescPermissions(descData));
1229
1230 jb = env->NewByteArray(descData.value().size());
1231 env->SetByteArrayRegion(jb, 0, descData.value().size(), (jbyte*)descData.value().data());
1232 success = javaDesc.callMethod<jboolean>("setLocalValue", jb);
1233 if (!success) {
1234 qCWarning(QT_BT_ANDROID) << "Cannot setup initial descriptor value for "
1235 << descData.uuid() << "(char" << charData.uuid()
1236 << "on service " << service->uuid << ")";
1237 }
1238
1239 env->DeleteLocalRef(jb);
1240
1241
1242 success = javaChar.callMethod<jboolean>("addDescriptor",
1243 javaDesc.object<QtJniTypes::BluetoothGattDescriptor>());
1244 if (!success) {
1245 qCWarning(QT_BT_ANDROID) << "Cannot add descriptor" << descData.uuid()
1246 << "to service" << service->uuid << "(char:"
1247 << charData.uuid() << ")";
1248 }
1249 }
1250
1251 success = service->androidService.callMethod<jboolean>(
1252 "addCharacteristic",
1253 javaChar.object<QtJniTypes::BluetoothGattCharacteristic>());
1254 if (!success) {
1255 qCWarning(QT_BT_ANDROID) << "Cannot add characteristic" << charData.uuid()
1256 << "to service" << service->uuid;
1257 }
1258 }
1259
1260 hub->javaObject().callMethod<void>("addService",
1261 service->androidService.object<QtJniTypes::BluetoothGattService>());
1262}
1263
1265{
1266 if (!hub) {
1267 qCWarning(QT_BT_ANDROID) << "could not determine MTU, hub is does not exist";
1268 return -1;
1269 } else {
1270 int result = hub->javaObject().callMethod<int>("mtu");
1271 qCDebug(QT_BT_ANDROID) << "MTU found to be" << result;
1272 return result;
1273 }
1274}
1275
1277{
1278 if (!hub || !hub->javaObject().callMethod<jboolean>("readRemoteRssi")) {
1279 qCWarning(QT_BT_ANDROID) << "request to read RSSI failed";
1281 return;
1282 }
1283}
1284
QT_BEGIN_NAMESPACE bool ensureAndroidPermission(QBluetoothPermission::CommunicationModes modes)
quint8 rssi
std::vector< ObjCStrongReference< CBMutableService > > services
void serviceDetailsDiscoveryFinished(const QString &serviceUuid, int startHandle, int endHandle)
void characteristicRead(const QBluetoothUuid &serviceUuid, int handle, const QBluetoothUuid &charUuid, int properties, const QByteArray &data)
void servicesDiscovered(QLowEnergyController::Error errorCode, const QString &uuids)
void serverCharacteristicChanged(const QJniObject &characteristic, const QByteArray &newValue)
void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode)
void serverDescriptorWritten(const QJniObject &descriptor, const QByteArray &newValue)
void characteristicChanged(int charHandle, const QByteArray &data)
void mtuChanged(int mtu)
void descriptorRead(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid, int handle, const QBluetoothUuid &descUuid, const QByteArray &data)
void remoteRssiRead(int rssi, bool success)
void characteristicWritten(int charHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode)
void descriptorWritten(int descHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode)
void connectionUpdated(QLowEnergyController::ControllerState newState, QLowEnergyController::Error errorCode)
void advertisementError(int status)
\inmodule QtBluetooth
\inmodule QtBluetooth
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
QByteArray toHex(char separator='\0') const
Returns a hex encoded copy of the byte array.
\inmodule QtCore
\inmodule QtCore
The QLowEnergyAdvertisingData class represents the data to be broadcast during Bluetooth Low Energy a...
The QLowEnergyAdvertisingParameters class represents the parameters used for Bluetooth Low Energy adv...
The QLowEnergyCharacteristicData class is used to set up GATT service data. \inmodule QtBluetooth.
QLowEnergyCharacteristic::PropertyTypes properties() const
Returns the properties of the characteristic.
PropertyType
This enum describes the properties of a characteristic.
bool isValid() const
Returns true if the QLowEnergyCharacteristic object is valid, otherwise returns false.
The QLowEnergyConnectionParameters class is used when requesting or reporting an update of the parame...
void writeDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, const QByteArray &newValue) override
void discoverServiceDetails(const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode) override
void readDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle) override
void startAdvertising(const QLowEnergyAdvertisingParameters &params, const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData) override
void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override
void readCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle) override
void requestConnectionUpdate(const QLowEnergyConnectionParameters &params) override
void writeCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QByteArray &newValue, QLowEnergyService::WriteMode mode) override
QLowEnergyCharacteristic characteristicForHandle(QLowEnergyHandle handle)
Returns a valid characteristic if the given handle is the handle of the characteristic itself or one ...
QSharedPointer< QLowEnergyServicePrivate > serviceForHandle(QLowEnergyHandle handle)
quint16 updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descriptorHandle, const QByteArray &value, bool appendValue)
Returns the length of the updated descriptor value.
QLowEnergyDescriptor descriptorForHandle(QLowEnergyHandle handle)
Returns a valid descriptor if handle belongs to a descriptor; otherwise an invalid one.
QLowEnergyController::Error error
void setError(QLowEnergyController::Error newError)
quint16 updateValueOfCharacteristic(QLowEnergyHandle charHandle, const QByteArray &value, bool appendValue)
Returns the length of the updated characteristic value.
QLowEnergyController::ControllerState state
void setState(QLowEnergyController::ControllerState newState)
\inmodule QtBluetooth
ControllerState
Indicates the state of the controller object.
Error
Indicates all possible error conditions found during the controller's existence.
The QLowEnergyDescriptorData class is used to create GATT service data. \inmodule QtBluetooth.
bool isReadable() const
Returns true if the value of this descriptor is readable and false otherwise.
QBluetooth::AttAccessConstraints readConstraints() const
Returns the constraints under which the value of this descriptor can be read.
bool isWritable() const
Returns true if the value of this descriptor is writable and false otherwise.
\inmodule QtBluetooth
bool isValid() const
Returns true if the QLowEnergyDescriptor object is valid, otherwise returns false.
The QLowEnergyServiceData class is used to set up GATT service data. \inmodule QtBluetooth.
QList< QLowEnergyCharacteristicData > characteristics() const
Returns the list of characteristics.
QList< QLowEnergyService * > includedServices() const
Returns the list of included services.
ServiceType type() const
Returns the type of this service.
DiscoveryMode
This enum lists service discovery modes.
ServiceError
This enum describes all possible error conditions during the service's existence.
WriteMode
This enum describes the mode to be used when writing a characteristic value.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1240
@ WithoutBraces
Definition quuid.h:54
QString toString(StringFormat mode=WithBraces) const
Definition quuid.cpp:650
bool isNull() const noexcept
Returns true if this is the null UUID {00000000-0000-0000-0000-000000000000}; otherwise returns false...
Definition quuid.cpp:818
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
Q_CORE_EXPORT QtJniTypes::Service service()
@ SkipEmptyParts
Definition qnamespace.h:128
quint16 QLowEnergyHandle
Definition qbluetooth.h:42
static const QCssKnownValue properties[NumProperties - 1]
#define qWarning
Definition qlogging.h:166
#define qCCritical(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
static QJniObject javaParcelUuidfromQtUuid(const QBluetoothUuid &uuid)
static int setupCharPermissions(const QLowEnergyCharacteristicData &charData)
QT_BEGIN_NAMESPACE const int BTLE_MAX_ATTRIBUTE_VALUE_SIZE
static QJniObject createJavaAdvertiseSettings(const QLowEnergyAdvertisingParameters &params)
static QJniObject createJavaAdvertiseData(const QLowEnergyAdvertisingData &data)
static QJniObject javaUuidfromQtUuid(const QBluetoothUuid &uuid)
static int setupDescPermissions(const QLowEnergyDescriptorData &descData)
void registerQLowEnergyControllerMetaType()
static const QMetaObjectPrivate * priv(const uint *data)
GLuint64 GLenum void * handle
GLenum mode
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
void ** params
GLuint entry
GLsizei const void * pointer
Definition qopenglext.h:384
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
int qint32
Definition qtypes.h:49
QT_BEGIN_NAMESPACE typedef uchar * output
QList< int > list
[14]
char * toString(const MyType &t)
[31]