5#include "android/androidutils_p.h"
6#include "android/jni_android_p.h"
7#include <QCoreApplication>
8#include <QtCore/QLoggingCategory>
9#include <QtCore/QJniEnvironment>
10#include <QtCore/QJniObject>
11#include <QtCore/q26numeric.h>
12#include <QtBluetooth/QLowEnergyServiceData>
13#include <QtBluetooth/QLowEnergyCharacteristicData>
14#include <QtBluetooth/QLowEnergyDescriptorData>
15#include <QtBluetooth/QLowEnergyAdvertisingData>
16#include <QtBluetooth/QLowEnergyAdvertisingParameters>
17#include <QtBluetooth/QLowEnergyConnectionParameters>
21Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
24const int BTLE_MAX_ATTRIBUTE_VALUE_SIZE = 512;
30 const QString output = uuid.toString(QUuid::WithoutBraces);
32 QJniObject javaString = QJniObject::fromString(output);
33 QJniObject javaUuid = QJniObject::callStaticMethod<QtJniTypes::UUID>(
34 QtJniTypes::Traits<QtJniTypes::UUID>::className(),
"fromString",
35 javaString.object<jstring>());
49 if (role == QLowEnergyController::PeripheralRole) {
51 hub->javaObject().callMethod<
void>(
"disconnectServer");
57 const bool isPeripheral = (role == QLowEnergyController::PeripheralRole);
60 qRegisterMetaType<QJniObject>();
61 hub =
new LowEnergyNotificationHub(remoteDevice, isPeripheral,
this);
64 connect(hub, &LowEnergyNotificationHub::connectionUpdated,
65 this, &QLowEnergyControllerPrivateAndroid::connectionUpdated);
70 connect(hub, &LowEnergyNotificationHub::serverCharacteristicChanged,
71 this, &QLowEnergyControllerPrivateAndroid::serverCharacteristicChanged);
72 connect(hub, &LowEnergyNotificationHub::serverDescriptorWritten,
73 this, &QLowEnergyControllerPrivateAndroid::serverDescriptorWritten);
75 hub =
new LowEnergyNotificationHub(remoteDevice, isPeripheral,
this);
77 connect(hub, &LowEnergyNotificationHub::connectionUpdated,
78 this, &QLowEnergyControllerPrivateAndroid::connectionUpdated);
81 connect(hub, &LowEnergyNotificationHub::servicesDiscovered,
82 this, &QLowEnergyControllerPrivateAndroid::servicesDiscovered);
83 connect(hub, &LowEnergyNotificationHub::serviceDetailsDiscoveryFinished,
84 this, &QLowEnergyControllerPrivateAndroid::serviceDetailsDiscoveryFinished);
85 connect(hub, &LowEnergyNotificationHub::characteristicRead,
86 this, &QLowEnergyControllerPrivateAndroid::characteristicRead);
87 connect(hub, &LowEnergyNotificationHub::descriptorRead,
88 this, &QLowEnergyControllerPrivateAndroid::descriptorRead);
89 connect(hub, &LowEnergyNotificationHub::characteristicWritten,
90 this, &QLowEnergyControllerPrivateAndroid::characteristicWritten);
91 connect(hub, &LowEnergyNotificationHub::descriptorWritten,
92 this, &QLowEnergyControllerPrivateAndroid::descriptorWritten);
93 connect(hub, &LowEnergyNotificationHub::characteristicChanged,
94 this, &QLowEnergyControllerPrivateAndroid::characteristicChanged);
95 connect(hub, &LowEnergyNotificationHub::serviceError,
96 this, &QLowEnergyControllerPrivateAndroid::serviceError);
105 qCCritical(QT_BT_ANDROID) <<
"connectToDevice() LE controller has not been initialized";
109 if (!ensureAndroidPermission(QBluetoothPermission::Access)) {
111 setError(QLowEnergyController::MissingPermissionsError);
112 qCWarning(QT_BT_ANDROID) <<
"connectToDevice() failed due to missing permissions";
117 if (remoteDevice.isNull()) {
118 qCWarning(QT_BT_ANDROID) <<
"Invalid/null remote device address";
119 setError(QLowEnergyController::UnknownRemoteDeviceError);
123 setState(QLowEnergyController::ConnectingState);
125 if (!hub->javaObject().isValid()) {
126 qCWarning(QT_BT_ANDROID) <<
"Cannot initiate QtBluetoothLE";
127 setError(QLowEnergyController::ConnectionError);
128 setState(QLowEnergyController::UnconnectedState);
132 bool result = hub->javaObject().callMethod<jboolean>(
"connect");
134 setError(QLowEnergyController::ConnectionError);
135 setState(QLowEnergyController::UnconnectedState);
143
144
145
146
148 QLowEnergyController::ControllerState oldState = state;
149 setState(QLowEnergyController::ClosingState);
152 if (role == QLowEnergyController::PeripheralRole)
153 hub->javaObject().callMethod<
void>(
"disconnectServer");
155 hub->javaObject().callMethod<
void>(
"disconnect");
158 if (oldState == QLowEnergyController::ConnectingState)
159 setState(QLowEnergyController::UnconnectedState);
166 if (hub && hub->javaObject().callMethod<jboolean>(
"discoverServices")) {
167 qCDebug(QT_BT_ANDROID) <<
"Service discovery initiated";
170 setError(QLowEnergyController::NetworkError);
171 setState(QLowEnergyController::ConnectedState);
176 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
179 if (!serviceList.contains(service)) {
180 qCWarning(QT_BT_ANDROID) <<
"Discovery of unknown service" << service.toString()
188 QString tempUuid = service.toString(QUuid::WithoutBraces);
191 QJniObject uuid = QJniObject::fromString(tempUuid);
192 bool readAllValues = mode == QLowEnergyService::FullDiscovery;
193 bool result = hub->javaObject().callMethod<jboolean>(
"discoverServiceDetails",
194 uuid.object<jstring>(),
197 QSharedPointer<QLowEnergyServicePrivate> servicePrivate =
198 serviceList.value(service);
199 if (!servicePrivate.isNull()) {
200 servicePrivate->setError(QLowEnergyService::UnknownError);
201 servicePrivate->setState(QLowEnergyService::RemoteService);
203 qCWarning(QT_BT_ANDROID) <<
"Cannot discover details for" << service.toString();
207 qCDebug(QT_BT_ANDROID) <<
"Discovery of" << service <<
"started";
211 const QSharedPointer<QLowEnergyServicePrivate> service,
213 const QByteArray &newValue,
214 QLowEnergyService::WriteMode mode)
217 Q_ASSERT(!service.isNull());
219 if (!service->characteristicList.contains(charHandle))
223 const jsize nativeSize = q26::saturate_cast<jsize>(newValue.size());
225 payload = env->NewByteArray(nativeSize);
226 env->SetByteArrayRegion(payload, 0, nativeSize,
227 (jbyte *)newValue.constData());
231 if (role == QLowEnergyController::CentralRole) {
232 qCDebug(QT_BT_ANDROID) <<
"Write characteristic with handle " << charHandle
233 << newValue.toHex() <<
"(service:" << service->uuid
234 <<
", writeWithResponse:" << (mode == QLowEnergyService::WriteWithResponse)
235 <<
", signed:" << (mode == QLowEnergyService::WriteSigned) <<
")";
236 result = hub->javaObject().callMethod<jboolean>(
"writeCharacteristic",
237 charHandle, payload, jint(mode));
239 qCDebug(QT_BT_ANDROID) <<
"Write server characteristic with handle " << charHandle
240 << newValue.toHex() <<
"(service:" << service->uuid;
242 const auto &characteristic = characteristicForHandle(charHandle);
243 if (characteristic.isValid()) {
244 const QJniObject charUuid = javaUuidfromQtUuid(characteristic.uuid());
245 result = hub->javaObject().callMethod<jboolean>(
246 "writeCharacteristic",
247 service->androidService.object<QtJniTypes::BluetoothGattService>(),
248 charUuid.object<QtJniTypes::UUID>(), payload);
250 service->characteristicList[charHandle].value = newValue;
255 env->DeleteLocalRef(payload);
258 service->setError(QLowEnergyService::CharacteristicWriteError);
262 const QSharedPointer<QLowEnergyServicePrivate> service,
265 const QByteArray &newValue)
267 Q_ASSERT(!service.isNull());
270 const jsize nativeSize = q26::saturate_cast<jsize>(newValue.size());
272 payload = env->NewByteArray(nativeSize);
273 env->SetByteArrayRegion(payload, 0, nativeSize,
274 (jbyte *)newValue.constData());
278 if (role == QLowEnergyController::CentralRole) {
279 qCDebug(QT_BT_ANDROID) <<
"Write descriptor with handle " << descHandle
280 << newValue.toHex() <<
"(service:" << service->uuid <<
")";
281 result = hub->javaObject().callMethod<jboolean>(
"writeDescriptor",
282 descHandle, payload);
284 const auto &characteristic = characteristicForHandle(charHandle);
285 const auto &descriptor = descriptorForHandle(descHandle);
286 if (characteristic.isValid() && descriptor.isValid()) {
287 qCDebug(QT_BT_ANDROID) <<
"Write descriptor" << descriptor.uuid()
288 <<
"(service:" << service->uuid
289 <<
"char: " << characteristic.uuid() <<
")";
290 const QJniObject charUuid = javaUuidfromQtUuid(characteristic.uuid());
291 const QJniObject descUuid = javaUuidfromQtUuid(descriptor.uuid());
292 result = hub->javaObject().callMethod<jboolean>(
294 service->androidService.object<QtJniTypes::BluetoothGattService>(),
295 charUuid.object<QtJniTypes::UUID>(), descUuid.object<QtJniTypes::UUID>(),
298 service->characteristicList[charHandle].descriptorList[descHandle].value = newValue;
303 env->DeleteLocalRef(payload);
306 service->setError(QLowEnergyService::DescriptorWriteError);
310 const QSharedPointer<QLowEnergyServicePrivate> service,
313 Q_ASSERT(!service.isNull());
315 if (!service->characteristicList.contains(charHandle))
321 qCDebug(QT_BT_ANDROID) <<
"Read characteristic with handle"
322 << charHandle << service->uuid;
323 result = hub->javaObject().callMethod<jboolean>(
"readCharacteristic", charHandle);
327 service->setError(QLowEnergyService::CharacteristicReadError);
331 const QSharedPointer<QLowEnergyServicePrivate> service,
335 Q_ASSERT(!service.isNull());
340 qCDebug(QT_BT_ANDROID) <<
"Read descriptor with handle"
341 << descriptorHandle << service->uuid;
342 result = hub->javaObject().callMethod<jboolean>(
"readDescriptor", descriptorHandle);
346 service->setError(QLowEnergyService::DescriptorReadError);
350 QLowEnergyController::ControllerState newState,
351 QLowEnergyController::Error errorCode)
353 qCDebug(QT_BT_ANDROID) <<
"Connection updated:"
354 <<
"error:" << errorCode
355 <<
"oldState:" << state
356 <<
"newState:" << newState;
358 if (role == QLowEnergyController::PeripheralRole)
359 peripheralConnectionUpdated(newState, errorCode);
361 centralConnectionUpdated(newState, errorCode);
366 Q_Q(QLowEnergyController);
367 qCDebug(QT_BT_ANDROID) <<
"MTU updated:"
369 emit q->mtuChanged(mtu);
374 Q_Q(QLowEnergyController);
378 emit q->rssiRead(rssi);
380 qCDebug(QT_BT_ANDROID) <<
"Reading remote RSSI failed";
381 setError(QLowEnergyController::RssiReadError);
387 QLowEnergyController::ControllerState newState,
388 QLowEnergyController::Error errorCode)
391 if (errorCode > QLowEnergyController::AdvertisingError)
392 errorCode = QLowEnergyController::UnknownError;
394 if (errorCode != QLowEnergyController::NoError)
397 const QLowEnergyController::ControllerState oldState = state;
401 if (newState == QLowEnergyController::UnconnectedState)
406 remoteDevice = QBluetoothAddress(
407 hub->javaObject().callMethod<jstring>(
"remoteAddress").toString());
408 remoteName = hub->javaObject().callMethod<jstring>(
"remoteName").toString();
411 Q_Q(QLowEnergyController);
413 if (oldState == QLowEnergyController::ConnectedState
414 && newState != QLowEnergyController::ConnectedState) {
415 emit q->disconnected();
416 }
else if (newState == QLowEnergyController::ConnectedState
417 && oldState != QLowEnergyController::ConnectedState) {
424 QLowEnergyController::ControllerState newState,
425 QLowEnergyController::Error errorCode)
427 Q_Q(QLowEnergyController);
429 const QLowEnergyController::ControllerState oldState = state;
431 if (errorCode != QLowEnergyController::NoError) {
433 if (oldState == QLowEnergyController::ConnectingState) {
434 setError(QLowEnergyController::ConnectionError);
436
437
438
439
440
441
442
443 newState = QLowEnergyController::UnconnectedState;
450 if (newState == QLowEnergyController::UnconnectedState
451 && !(oldState == QLowEnergyController::UnconnectedState
452 || oldState == QLowEnergyController::ConnectingState)) {
457 if (!serviceList.isEmpty()) {
458 Q_ASSERT(oldState != QLowEnergyController::ClosingState);
461 emit q->disconnected();
462 }
else if (newState == QLowEnergyController::ConnectedState
463 && oldState != QLowEnergyController::ConnectedState ) {
469 QLowEnergyController::Error errorCode,
const QString &foundServices)
471 Q_Q(QLowEnergyController);
473 if (errorCode == QLowEnergyController::NoError) {
475 const QStringList list = foundServices.split(QChar::Space, Qt::SkipEmptyParts);
476 for (
const QString &entry : list) {
477 const QBluetoothUuid service(entry);
478 if (service.isNull())
481 QLowEnergyServicePrivate *priv =
new QLowEnergyServicePrivate();
482 priv->uuid = service;
483 priv->setController(
this);
485 QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
486 serviceList.insert(service, pointer);
488 emit q->serviceDiscovered(QBluetoothUuid(entry));
491 setState(QLowEnergyController::DiscoveredState);
492 emit q->discoveryFinished();
495 setState(QLowEnergyController::ConnectedState);
500 const QString &serviceUuid,
int startHandle,
int endHandle)
502 const QBluetoothUuid service(serviceUuid);
503 if (!serviceList.contains(service)) {
504 qCWarning(QT_BT_ANDROID) <<
"Discovery done of unknown service:"
505 << service.toString();
510 QSharedPointer<QLowEnergyServicePrivate> pointer =
511 serviceList.value(service);
512 pointer->startHandle = startHandle;
513 pointer->endHandle = endHandle;
515 if (hub && hub->javaObject().isValid()) {
516 QJniObject uuid = QJniObject::fromString(serviceUuid);
517 QJniObject javaIncludes = hub->javaObject().callMethod<jstring>(
518 "includedServices", uuid.object<jstring>());
519 if (javaIncludes.isValid()) {
520 const QStringList list = javaIncludes.toString()
521 .split(QChar::Space, Qt::SkipEmptyParts);
522 for (
const QString &entry : list) {
523 const QBluetoothUuid service(entry);
524 if (service.isNull())
527 pointer->includedServices.append(service);
530 QSharedPointer<QLowEnergyServicePrivate> otherService =
531 serviceList.value(service);
532 if (!otherService.isNull())
533 otherService->type |= QLowEnergyService::IncludedService;
538 qCDebug(QT_BT_ANDROID) <<
"Service" << serviceUuid <<
"discovered (start:"
539 << startHandle <<
"end:" << endHandle <<
")" << pointer.data();
541 pointer->setState(QLowEnergyService::RemoteServiceDiscovered);
545 const QBluetoothUuid &serviceUuid,
int handle,
546 const QBluetoothUuid &charUuid,
int properties,
const QByteArray &data)
548 if (!serviceList.contains(serviceUuid))
551 QSharedPointer<QLowEnergyServicePrivate> service =
552 serviceList.value(serviceUuid);
556 service->characteristicList[charHandle];
559 charDetails.properties = QLowEnergyCharacteristic::PropertyType(properties);
560 charDetails.uuid = charUuid;
561 charDetails.value = data;
563 charDetails.valueHandle = charHandle + 1;
565 if (service->state == QLowEnergyService::RemoteServiceDiscovered) {
566 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
567 if (!characteristic.isValid()) {
568 qCWarning(QT_BT_ANDROID) <<
"characteristicRead: Cannot find characteristic";
571 emit service->characteristicRead(characteristic, data);
576 const QBluetoothUuid &serviceUuid,
const QBluetoothUuid &charUuid,
577 int descHandle,
const QBluetoothUuid &descUuid,
const QByteArray &data)
579 if (!serviceList.contains(serviceUuid))
582 QSharedPointer<QLowEnergyServicePrivate> service =
583 serviceList.value(serviceUuid);
585 bool entryUpdated =
false;
587 CharacteristicDataMap::iterator charIt = service->characteristicList.begin();
588 for ( ; charIt != service->characteristicList.end(); ++charIt) {
591 if (charDetails.uuid != charUuid)
596 charDetails.descriptorList[descHandle];
597 descDetails.uuid = descUuid;
598 descDetails.value = data;
604 qCWarning(QT_BT_ANDROID) <<
"Cannot find/update descriptor"
605 << descUuid << charUuid << serviceUuid;
606 }
else if (service->state == QLowEnergyService::RemoteServiceDiscovered){
607 QLowEnergyDescriptor descriptor = descriptorForHandle(descHandle);
608 if (!descriptor.isValid()) {
609 qCWarning(QT_BT_ANDROID) <<
"descriptorRead: Cannot find descriptor";
612 emit service->descriptorRead(descriptor, data);
617 int charHandle,
const QByteArray &data, QLowEnergyService::ServiceError errorCode)
619 QSharedPointer<QLowEnergyServicePrivate> service =
620 serviceForHandle(charHandle);
621 if (service.isNull())
624 qCDebug(QT_BT_ANDROID) <<
"Characteristic write confirmation" << service->uuid
625 << charHandle << data.toHex() << errorCode;
627 if (errorCode != QLowEnergyService::NoError) {
628 service->setError(errorCode);
632 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
633 if (!characteristic.isValid()) {
634 qCWarning(QT_BT_ANDROID) <<
"characteristicWritten: Cannot find characteristic";
640 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
641 updateValueOfCharacteristic(charHandle, data,
false);
642 emit service->characteristicWritten(characteristic, data);
646 int descHandle,
const QByteArray &data, QLowEnergyService::ServiceError errorCode)
648 QSharedPointer<QLowEnergyServicePrivate> service =
649 serviceForHandle(descHandle);
650 if (service.isNull())
653 qCDebug(QT_BT_ANDROID) <<
"Descriptor write confirmation" << service->uuid
654 << descHandle << data.toHex() << errorCode;
656 if (errorCode != QLowEnergyService::NoError) {
657 service->setError(errorCode);
661 QLowEnergyDescriptor descriptor = descriptorForHandle(descHandle);
662 if (!descriptor.isValid()) {
663 qCWarning(QT_BT_ANDROID) <<
"descriptorWritten: Cannot find descriptor";
667 updateValueOfDescriptor(descriptor.characteristicHandle(),
668 descHandle, data,
false);
669 emit service->descriptorWritten(descriptor, data);
673 const QJniObject &jniDesc,
const QByteArray &newValue)
675 qCDebug(QT_BT_ANDROID) <<
"Server descriptor change notification" << newValue.toHex();
678 const QJniObject jniChar = jniDesc.callMethod<QtJniTypes::BluetoothGattCharacteristic>(
679 "getCharacteristic");
680 if (!jniChar.isValid())
683 const QJniObject jniService =
684 jniChar.callMethod<QtJniTypes::BluetoothGattService>(
"getService");
685 if (!jniService.isValid())
688 QJniObject jniUuid = jniService.callMethod<QtJniTypes::UUID>(
"getUuid");
689 const QBluetoothUuid serviceUuid(jniUuid.toString());
690 if (serviceUuid.isNull())
694 if (!localServices.contains(serviceUuid))
697 jniUuid = jniChar.callMethod<QtJniTypes::UUID>(
"getUuid");
698 const QBluetoothUuid characteristicUuid(jniUuid.toString());
699 if (characteristicUuid.isNull())
702 jniUuid = jniDesc.callMethod<QtJniTypes::UUID>(
"getUuid");
703 const QBluetoothUuid descriptorUuid(jniUuid.toString());
704 if (descriptorUuid.isNull())
708 auto servicePrivate = localServices.value(serviceUuid);
711 const auto handleList = servicePrivate->characteristicList.keys();
712 for (
const auto charHandle: handleList) {
713 const auto &charData = servicePrivate->characteristicList.value(charHandle);
714 if (charData.uuid != characteristicUuid)
717 const auto &descHandleList = charData.descriptorList.keys();
718 for (
const auto descHandle: descHandleList) {
719 const auto &descData = charData.descriptorList.value(descHandle);
720 if (descData.uuid != descriptorUuid)
723 qCDebug(QT_BT_ANDROID) <<
"serverDescriptorChanged: Matching descriptor"
724 << descriptorUuid <<
"in char" << characteristicUuid
725 <<
"of service" << serviceUuid;
727 servicePrivate->characteristicList[charHandle].descriptorList[descHandle].value = newValue;
729 emit servicePrivate->descriptorWritten(
730 QLowEnergyDescriptor(servicePrivate, charHandle, descHandle),
738 int charHandle,
const QByteArray &data)
740 QSharedPointer<QLowEnergyServicePrivate> service =
741 serviceForHandle(charHandle);
742 if (service.isNull())
745 qCDebug(QT_BT_ANDROID) <<
"Characteristic change notification" << service->uuid
746 << charHandle << data.toHex() <<
"length:" << data.size();
748 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
749 if (!characteristic.isValid()) {
750 qCWarning(QT_BT_ANDROID) <<
"characteristicChanged: Cannot find characteristic";
756 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
757 updateValueOfCharacteristic(characteristic.attributeHandle(),
759 emit service->characteristicChanged(characteristic, data);
763 const QJniObject &characteristic,
const QByteArray &newValue)
765 qCDebug(QT_BT_ANDROID) <<
"Server characteristic change notification"
766 << newValue.toHex() <<
"length:" << newValue.size();
769 QJniObject service = characteristic.callMethod<QtJniTypes::BluetoothGattService>(
771 if (!service.isValid())
774 QJniObject jniUuid = service.callMethod<QtJniTypes::UUID>(
"getUuid");
775 QBluetoothUuid serviceUuid(jniUuid.toString());
776 if (serviceUuid.isNull())
780 if (!localServices.contains(serviceUuid))
783 auto servicePrivate = localServices.value(serviceUuid);
785 jniUuid = characteristic.callMethod<QtJniTypes::UUID>(
"getUuid");
786 QBluetoothUuid characteristicUuid(jniUuid.toString());
787 if (characteristicUuid.isNull())
791 const auto handleList = servicePrivate->characteristicList.keys();
793 for (
const auto handle: handleList) {
794 QLowEnergyServicePrivate::CharData &charData = servicePrivate->characteristicList[handle];
795 if (charData.uuid != characteristicUuid)
798 qCDebug(QT_BT_ANDROID) <<
"serverCharacteristicChanged: Matching characteristic"
799 << characteristicUuid <<
" on " << serviceUuid;
800 charData.value = newValue;
801 foundHandle = handle;
808 emit servicePrivate->characteristicChanged(
809 QLowEnergyCharacteristic(servicePrivate, foundHandle), newValue);
813 int attributeHandle, QLowEnergyService::ServiceError errorCode)
816 if (errorCode == QLowEnergyService::NoError)
819 QSharedPointer<QLowEnergyServicePrivate> service =
820 serviceForHandle(attributeHandle);
821 Q_ASSERT(!service.isNull());
825 service->setError(errorCode);
830 Q_Q(QLowEnergyController);
835 errorString = QLowEnergyController::tr(
"Advertisement data is larger than 31 bytes");
838 errorString = QLowEnergyController::tr(
"Advertisement feature not supported on the platform");
841 errorString = QLowEnergyController::tr(
"Error occurred trying to start advertising");
844 errorString = QLowEnergyController::tr(
"Failed due to too many advertisers");
847 errorString = QLowEnergyController::tr(
"Unknown advertisement error");
851 error = QLowEnergyController::AdvertisingError;
852 emit q->errorOccurred(error);
855 Q_ASSERT(state != QLowEnergyController::DiscoveredState);
856 Q_ASSERT(state != QLowEnergyController::DiscoveringState);
860 case QLowEnergyController::UnconnectedState:
861 case QLowEnergyController::ConnectingState:
862 case QLowEnergyController::ConnectedState:
863 case QLowEnergyController::ClosingState:
868 case QLowEnergyController::AdvertisingState:
869 setState(QLowEnergyController::UnconnectedState);
878 QString output = uuid.toString();
880 output = output.mid(1, output.size()-2);
882 QJniObject javaString = QJniObject::fromString(output);
883 QJniObject parcelUuid = QJniObject::callStaticMethod<QtJniTypes::ParcelUuid>(
884 QtJniTypes::Traits<QtJniTypes::ParcelUuid>::className(),
"fromString",
885 javaString.object<jstring>());
892 QJniObject builder = QJniObject::construct<QtJniTypes::AdvertiseDataBuilder>();
895 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>(
896 "setIncludeDeviceName", !data.localName().isEmpty());
897 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>(
898 "setIncludeTxPowerLevel", data.includePowerLevel());
899 const auto services = data.services();
900 for (
const auto &service : services) {
901 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>(
"addServiceUuid",
902 javaParcelUuidfromQtUuid(service).object<QtJniTypes::ParcelUuid>());
905 if (!data.manufacturerData().isEmpty()) {
907 const jsize nativeSize = q26::saturate_cast<jsize>(data.manufacturerData().size());
908 jbyteArray nativeData = env->NewByteArray(nativeSize);
909 env->SetByteArrayRegion(nativeData, 0, nativeSize,
910 reinterpret_cast<
const jbyte*>(data.manufacturerData().constData()));
911 builder = builder.callMethod<QtJniTypes::AdvertiseDataBuilder>(
912 "addManufacturerData", jint(data.manufacturerId()), nativeData);
913 env->DeleteLocalRef(nativeData);
915 if (!builder.isValid()) {
916 qCWarning(QT_BT_ANDROID) <<
"Cannot set manufacturer id/data";
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
939 QJniObject javaAdvertiseData = builder.callMethod<QtJniTypes::AdvertiseData>(
"build");
940 return javaAdvertiseData;
945 QJniObject builder = QJniObject::construct<QtJniTypes::AdvertiseSettingsBuilder>();
947 bool connectable =
false;
948 switch (params.mode())
950 case QLowEnergyAdvertisingParameters::AdvInd:
953 case QLowEnergyAdvertisingParameters::AdvScanInd:
954 case QLowEnergyAdvertisingParameters::AdvNonConnInd:
959 builder = builder.callMethod<QtJniTypes::AdvertiseSettingsBuilder>(
960 "setConnectable", connectable);
963
964
966 QJniObject javaAdvertiseSettings = builder.callMethod<QtJniTypes::AdvertiseSettings>(
"build");
967 return javaAdvertiseSettings;
972 const QLowEnergyAdvertisingData &advertisingData,
973 const QLowEnergyAdvertisingData &scanResponseData)
975 setState(QLowEnergyController::AdvertisingState);
977 if (!ensureAndroidPermission(QBluetoothPermission::Access | QBluetoothPermission::Advertise)) {
978 qCWarning(QT_BT_ANDROID) <<
"startAdvertising() failed due to missing permissions";
979 setError(QLowEnergyController::MissingPermissionsError);
980 setState(QLowEnergyController::UnconnectedState);
984 if (!hub || !hub->javaObject().isValid()) {
985 qCWarning(QT_BT_ANDROID) <<
"Cannot initiate QtBluetoothLEServer";
986 setError(QLowEnergyController::AdvertisingError);
987 setState(QLowEnergyController::UnconnectedState);
992 QJniObject jAdvertiseData = createJavaAdvertiseData(advertisingData);
993 QJniObject jScanResponse = createJavaAdvertiseData(scanResponseData);
994 QJniObject jAdvertiseSettings = createJavaAdvertiseSettings(params);
996 const bool result = hub->javaObject().callMethod<jboolean>(
"startAdvertising",
997 jAdvertiseData.object<QtJniTypes::AdvertiseData>(),
998 jScanResponse.object<QtJniTypes::AdvertiseData>(),
999 jAdvertiseSettings.object<QtJniTypes::AdvertiseSettings>());
1001 setError(QLowEnergyController::AdvertisingError);
1002 setState(QLowEnergyController::UnconnectedState);
1008 setState(QLowEnergyController::UnconnectedState);
1009 hub->javaObject().callMethod<
void>(
"stopAdvertising");
1023 if (role != QLowEnergyController::CentralRole) {
1024 qCWarning(QT_BT_ANDROID) <<
"On Android, connection requests only work for central role";
1028 const bool result = hub->javaObject().callMethod<jboolean>(
"requestConnectionUpdatePriority",
1029 params.minimumInterval());
1031 qCWarning(QT_BT_ANDROID) <<
"Cannot set connection update priority";
1035
1036
1040 if (charData.properties() & QLowEnergyCharacteristic::Read) {
1041 if (
int(charData.readConstraints()) == 0
1042 || (charData.readConstraints()
1043 & QBluetooth::AttAccessConstraint::AttAuthorizationRequired)) {
1044 permission |= QJniObject::getStaticField<jint>(
1045 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1049 if (charData.readConstraints()
1050 & QBluetooth::AttAccessConstraint::AttAuthenticationRequired) {
1051 permission |= QJniObject::getStaticField<jint>(
1052 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1053 "PERMISSION_READ_ENCRYPTED");
1056 if (charData.readConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1057 permission |= QJniObject::getStaticField<jint>(
1058 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1059 "PERMISSION_READ_ENCRYPTED_MITM");
1063 if (charData.properties() &
1064 (QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse) ) {
1065 if (
int(charData.writeConstraints()) == 0
1066 || (charData.writeConstraints()
1067 & QBluetooth::AttAccessConstraint::AttAuthorizationRequired)) {
1068 permission |= QJniObject::getStaticField<jint>(
1069 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1070 "PERMISSION_WRITE");
1073 if (charData.writeConstraints()
1074 & QBluetooth::AttAccessConstraint::AttAuthenticationRequired) {
1075 permission |= QJniObject::getStaticField<jint>(
1076 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1077 "PERMISSION_WRITE_ENCRYPTED");
1080 if (charData.writeConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1081 permission |= QJniObject::getStaticField<jint>(
1082 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1083 "PERMISSION_WRITE_ENCRYPTED_MITM");
1087 if (charData.properties() & QLowEnergyCharacteristic::WriteSigned) {
1088 if (charData.writeConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1089 permission |= QJniObject::getStaticField<jint>(
1090 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1091 "PERMISSION_WRITE_SIGNED_MITM");
1093 permission |= QJniObject::getStaticField<jint>(
1094 QtJniTypes::Traits<QtJniTypes::BluetoothGattCharacteristic>::className(),
1095 "PERMISSION_WRITE_SIGNED");
1103
1104
1107 int permissions = 0;
1109 if (descData.isReadable()) {
1110 if (
int(descData.readConstraints()) == 0
1111 || (descData.readConstraints()
1112 & QBluetooth::AttAccessConstraint::AttAuthorizationRequired)) {
1113 permissions |= QJniObject::getStaticField<jint>(
1114 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1118 if (descData.readConstraints()
1119 & QBluetooth::AttAccessConstraint::AttAuthenticationRequired) {
1120 permissions |= QJniObject::getStaticField<jint>(
1121 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1122 "PERMISSION_READ_ENCRYPTED");
1125 if (descData.readConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1126 permissions |= QJniObject::getStaticField<jint>(
1127 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1128 "PERMISSION_READ_ENCRYPTED_MITM");
1132 if (descData.isWritable()) {
1133 if (
int(descData.readConstraints()) == 0
1134 || (descData.readConstraints()
1135 & QBluetooth::AttAccessConstraint::AttAuthorizationRequired)) {
1136 permissions |= QJniObject::getStaticField<jint>(
1137 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1138 "PERMISSION_WRITE");
1141 if (descData.readConstraints()
1142 & QBluetooth::AttAccessConstraint::AttAuthenticationRequired) {
1143 permissions |= QJniObject::getStaticField<jint>(
1144 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1145 "PERMISSION_WRITE_ENCRYPTED");
1148 if (descData.readConstraints() & QBluetooth::AttAccessConstraint::AttEncryptionRequired) {
1149 permissions |= QJniObject::getStaticField<jint>(
1150 QtJniTypes::Traits<QtJniTypes::BluetoothGattDescriptor>::className(),
1151 "PERMISSION_WRITE_ENCRYPTED_MITM");
1161 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(startHandle);
1162 if (service.isNull())
1166 jint sType = QJniObject::getStaticField<jint>(
1167 QtJniTypes::Traits<QtJniTypes::BluetoothGattService>::className(),
1168 "SERVICE_TYPE_PRIMARY");
1169 if (serviceData.type() == QLowEnergyServiceData::ServiceTypeSecondary)
1170 sType = QJniObject::getStaticField<jint>(
1171 QtJniTypes::Traits<QtJniTypes::BluetoothGattService>::className(),
1172 "SERVICE_TYPE_SECONDARY");
1174 service->androidService = QJniObject::construct<QtJniTypes::BluetoothGattService>(
1175 javaUuidfromQtUuid(service->uuid).object<QtJniTypes::UUID>(), sType);
1178 const QList<QLowEnergyService*> includedServices = serviceData.includedServices();
1179 for (
const auto includedServiceEntry: includedServices) {
1181 const jboolean result = service->androidService.callMethod<jboolean>(
"addService",
1182 includedServiceEntry->d_ptr->androidService.object<QtJniTypes::BluetoothGattService>());
1184 qWarning(QT_BT_ANDROID) <<
"Cannot add included service " << includedServiceEntry->serviceUuid()
1185 <<
"to current service" << service->uuid;
1189 const QList<QLowEnergyCharacteristicData> serviceCharsData = serviceData.characteristics();
1190 for (
const auto &charData: serviceCharsData) {
1196 if (charData.value().size() < charData.minimumValueLength() ||
1197 charData.value().size() > charData.maximumValueLength()) {
1198 qWarning() <<
"Warning: Ignoring characteristic" << charData.uuid()
1199 <<
"with invalid length:" << charData.value().size()
1200 <<
"(minimum:" << charData.minimumValueLength()
1201 <<
"maximum:" << charData.maximumValueLength() <<
").";
1206 if (charData.value().size() > BTLE_MAX_ATTRIBUTE_VALUE_SIZE) {
1207 qCWarning(QT_BT_ANDROID) <<
"Warning: characteristic" << charData.uuid() <<
"size"
1208 <<
"exceeds the standard: " << BTLE_MAX_ATTRIBUTE_VALUE_SIZE
1209 <<
", value size:" << charData.value().size();
1212 QJniObject javaChar = QJniObject::construct<QtJniTypes::QtBtGattCharacteristic>(
1213 javaUuidfromQtUuid(charData.uuid()).object<QtJniTypes::UUID>(),
1214 int(charData.properties()),
1215 setupCharPermissions(charData),
1216 charData.minimumValueLength(),
1217 charData.maximumValueLength());
1219 QJniEnvironment env;
1220 const jsize nativeCharSize = q26::saturate_cast<jsize>(charData.value().size());
1221 jbyteArray jb = env->NewByteArray(nativeCharSize);
1222 env->SetByteArrayRegion(jb, 0, nativeCharSize, (jbyte*)charData.value().data());
1223 jboolean success = javaChar.callMethod<jboolean>(
"setLocalValue", jb);
1225 qCWarning(QT_BT_ANDROID) <<
"Cannot setup initial characteristic value for " << charData.uuid();
1226 env->DeleteLocalRef(jb);
1228 const QList<QLowEnergyDescriptorData> descriptorList = charData.descriptors();
1229 for (
const auto &descData: descriptorList) {
1230 QJniObject javaDesc = QJniObject::construct<QtJniTypes::QtBtGattDescriptor>(
1231 javaUuidfromQtUuid(descData.uuid()).object<QtJniTypes::UUID>(),
1232 setupDescPermissions(descData));
1234 const jsize nativeDescSize = q26::saturate_cast<jsize>(descData.value().size());
1235 jb = env->NewByteArray(nativeDescSize);
1236 env->SetByteArrayRegion(jb, 0, nativeDescSize, (jbyte*)descData.value().data());
1237 success = javaDesc.callMethod<jboolean>(
"setLocalValue", jb);
1239 qCWarning(QT_BT_ANDROID) <<
"Cannot setup initial descriptor value for "
1240 << descData.uuid() <<
"(char" << charData.uuid()
1241 <<
"on service " << service->uuid <<
")";
1244 env->DeleteLocalRef(jb);
1247 success = javaChar.callMethod<jboolean>(
"addDescriptor",
1248 javaDesc.object<QtJniTypes::BluetoothGattDescriptor>());
1250 qCWarning(QT_BT_ANDROID) <<
"Cannot add descriptor" << descData.uuid()
1251 <<
"to service" << service->uuid <<
"(char:"
1252 << charData.uuid() <<
")";
1256 success = service->androidService.callMethod<jboolean>(
1257 "addCharacteristic",
1258 javaChar.object<QtJniTypes::BluetoothGattCharacteristic>());
1260 qCWarning(QT_BT_ANDROID) <<
"Cannot add characteristic" << charData.uuid()
1261 <<
"to service" << service->uuid;
1265 hub->javaObject().callMethod<
void>(
"addService",
1266 service->androidService.object<QtJniTypes::BluetoothGattService>());
1272 qCWarning(QT_BT_ANDROID) <<
"could not determine MTU, hub is does not exist";
1275 int result = hub->javaObject().callMethod<
int>(
"mtu");
1276 qCDebug(QT_BT_ANDROID) <<
"MTU found to be" << result;
1283 if (!hub || !hub->javaObject().callMethod<jboolean>(
"readRemoteRssi")) {
1284 qCWarning(QT_BT_ANDROID) <<
"request to read RSSI failed";
1285 setError(QLowEnergyController::RssiReadError);
void remoteRssiRead(int rssi, bool success)
void advertisementError(int status)
void writeDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, const QByteArray &newValue) override
void connectToDevice() override
void discoverServices() override
~QLowEnergyControllerPrivateAndroid() override
void discoverServiceDetails(const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode) override
QLowEnergyControllerPrivateAndroid()
void disconnectFromDevice() override
void readDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle) override
void startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData) override
void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override
void stopAdvertising() override
void readCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle) override
void requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) override
void writeCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QByteArray &newValue, QLowEnergyService::WriteMode mode) override
void invalidateServices()
static QJniObject javaParcelUuidfromQtUuid(const QBluetoothUuid &uuid)
static int setupCharPermissions(const QLowEnergyCharacteristicData &charData)
static QJniObject createJavaAdvertiseSettings(const QLowEnergyAdvertisingParameters ¶ms)
static QJniObject createJavaAdvertiseData(const QLowEnergyAdvertisingData &data)
static QJniObject javaUuidfromQtUuid(const QBluetoothUuid &uuid)
static int setupDescPermissions(const QLowEnergyDescriptorData &descData)
void registerQLowEnergyControllerMetaType()