5#include "bluez/adapter1_bluez5_p.h"
6#include "bluez/bluez5_helper_p.h"
7#include "bluez/device1_bluez5_p.h"
8#include "bluez/gattservice1_p.h"
9#include "bluez/gattchar1_p.h"
10#include "bluez/gattdesc1_p.h"
11#include "bluez/battery1_p.h"
12#include "bluez/objectmanager_p.h"
13#include "bluez/properties_p.h"
14#include "bluez/bluezperipheralapplication_p.h"
15#include "bluez/bluezperipheralconnectionmanager_p.h"
19Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
21using namespace QtBluetoothPrivate;
24 const QString &adapterPathWithPeripheralSupport)
26 adapterPathWithPeripheralSupport(adapterPathWithPeripheralSupport)
32 if (state != QLowEnergyController::UnconnectedState) {
33 qCWarning(QT_BT_BLUEZ) <<
"Low Energy Controller is not Unconnected when deleted."
34 <<
"Deleted in state:" << state;
40 if (role == QLowEnergyController::PeripheralRole) {
41 Q_ASSERT(!adapterPathWithPeripheralSupport.isEmpty());
43 peripheralApplication =
new QtBluezPeripheralApplication(adapterPathWithPeripheralSupport,
46 QObject::connect(peripheralApplication, &QtBluezPeripheralApplication::errorOccurred,
this,
47 &QLowEnergyControllerPrivateBluezDBus::handlePeripheralApplicationError);
49 QObject::connect(peripheralApplication, &QtBluezPeripheralApplication::registered,
this,
50 &QLowEnergyControllerPrivateBluezDBus::handlePeripheralApplicationRegistered);
52 QObject::connect(peripheralApplication,
53 &QtBluezPeripheralApplication::characteristicValueUpdatedByRemote,
this,
54 &QLowEnergyControllerPrivateBluezDBus::handlePeripheralCharacteristicValueUpdate);
56 QObject::connect(peripheralApplication,
57 &QtBluezPeripheralApplication::descriptorValueUpdatedByRemote,
this,
58 &QLowEnergyControllerPrivateBluezDBus::handlePeripheralDescriptorValueUpdate);
60 peripheralConnectionManager =
61 new QtBluezPeripheralConnectionManager(localAdapter,
this);
63 QObject::connect(peripheralApplication,
64 &QtBluezPeripheralApplication::remoteDeviceAccessEvent,
65 peripheralConnectionManager,
66 &QtBluezPeripheralConnectionManager::remoteDeviceAccessEvent);
68 QObject::connect(peripheralConnectionManager,
69 &QtBluezPeripheralConnectionManager::connectivityStateChanged,
this,
70 &QLowEnergyControllerPrivateBluezDBus::handlePeripheralConnectivityChanged);
72 QObject::connect(peripheralConnectionManager,
73 &QtBluezPeripheralConnectionManager::remoteDeviceChanged,
this,
74 &QLowEnergyControllerPrivateBluezDBus::handlePeripheralRemoteDeviceChanged);
79 const QString &interface,
const QVariantMap &changedProperties,
82 if (interface == QStringLiteral(
"org.bluez.Device1")) {
83 qCDebug(QT_BT_BLUEZ) <<
"######" << interface << changedProperties;
84 if (changedProperties.contains(QStringLiteral(
"ServicesResolved"))) {
89 bool isResolved = changedProperties.value(QStringLiteral(
"ServicesResolved")).toBool();
91 setState(QLowEnergyController::ConnectedState);
92 pendingConnect =
false;
93 disconnectSignalRequired =
true;
94 Q_Q(QLowEnergyController);
100 if (changedProperties.contains(QStringLiteral(
"Connected"))) {
101 bool isConnected = changedProperties.value(QStringLiteral(
"Connected")).toBool();
104 case QLowEnergyController::ConnectingState:
105 case QLowEnergyController::ConnectedState:
106 case QLowEnergyController::DiscoveringState:
107 case QLowEnergyController::DiscoveredState:
108 case QLowEnergyController::ClosingState:
110 QLowEnergyController::Error newError = QLowEnergyController::NoError;
112 newError = QLowEnergyController::ConnectionError;
114 executeClose(newError);
117 case QLowEnergyController::AdvertisingState:
118 case QLowEnergyController::UnconnectedState:
125 if (changedProperties.contains(QStringLiteral(
"UUIDs"))) {
126 const QStringList newUuidStringList = changedProperties.value(QStringLiteral(
"UUIDs")).toStringList();
127 QList<QBluetoothUuid> newUuidList;
128 for (
const QString &uuidString : newUuidStringList)
129 newUuidList.append(QBluetoothUuid(uuidString));
131 const auto serviceKeys = serviceList.keys();
132 for (
const QBluetoothUuid &uuid : serviceKeys) {
133 if (!newUuidList.contains(uuid)) {
134 qCDebug(QT_BT_BLUEZ) <<
__func__ <<
"Service" << uuid <<
"has been removed";
135 QSharedPointer<QLowEnergyServicePrivate> service = serviceList.take(uuid);
136 service->setController(
nullptr);
137 dbusServices.remove(uuid);
142 }
else if (interface == QStringLiteral(
"org.bluez.Battery1")) {
143 qCDebug(QT_BT_BLUEZ) <<
"######" << interface << changedProperties;
144 if (changedProperties.contains(QStringLiteral(
"Percentage"))) {
147 const QBluetoothUuid uuid(QBluetoothUuid::ServiceClassUuid::BatteryService);
148 if (!serviceList.contains(uuid) || !dbusServices.contains(uuid)
149 || !dbusServices[uuid].hasBatteryService
150 || dbusServices[uuid].batteryInterface.isNull())
153 QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(uuid);
154 if (serviceData->state != QLowEnergyService::RemoteServiceDiscovered)
157 QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData>::iterator iter;
158 iter = serviceData->characteristicList.begin();
159 while (iter != serviceData->characteristicList.end()) {
160 auto &charData = iter.value();
161 if (charData.uuid != QBluetoothUuid::CharacteristicType::BatteryLevel)
165 bool cccActive =
false;
166 for (
const QLowEnergyServicePrivate::DescData &descData : std::as_const(charData.descriptorList)) {
167 if (descData.uuid != QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration))
169 if (descData.value == QByteArray::fromHex(
"0100")
170 || descData.value == QByteArray::fromHex(
"0200")) {
176 const QByteArray newValue(1,
char(dbusServices[uuid].batteryInterface->percentage()));
177 qCDebug(QT_BT_BLUEZ) <<
"Battery1 char update" << cccActive
178 << charData.value.toHex() <<
"->" << newValue.toHex();
179 if (cccActive && newValue != charData.value) {
180 qCDebug(QT_BT_BLUEZ) <<
"Property update for Battery1";
181 charData.value = newValue;
182 QLowEnergyCharacteristic ch(serviceData, iter.key());
183 emit serviceData->characteristicChanged(ch, newValue);
193 QLowEnergyHandle charHandle,
const QString &interface,
194 const QVariantMap &changedProperties,
199 if (interface != QStringLiteral(
"org.bluez.GattCharacteristic1"))
202 if (!changedProperties.contains(QStringLiteral(
"Value")))
205 const QLowEnergyCharacteristic changedChar = characteristicForHandle(charHandle);
206 const QLowEnergyDescriptor ccnDescriptor = changedChar.descriptor(
207 QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
208 if (!ccnDescriptor.isValid())
211 const QByteArray newValue = changedProperties.value(QStringLiteral(
"Value")).toByteArray();
212 if (changedChar.properties() & QLowEnergyCharacteristic::Read)
213 updateValueOfCharacteristic(charHandle, newValue,
false);
215 auto service = serviceForHandle(charHandle);
217 if (!service.isNull())
218 emit service->characteristicChanged(changedChar, newValue);
222 const QStringList &interfaces)
224 if (objectPath.path() == device->path()) {
225 if (interfaces.contains(QStringLiteral(
"org.bluez.Device1"))) {
226 qCWarning(QT_BT_BLUEZ) <<
"DBus Device1 was removed";
227 executeClose(QLowEnergyController::UnknownRemoteDeviceError);
229 qCDebug(QT_BT_BLUEZ) <<
"DBus interfaces" << interfaces <<
"were removed from"
230 << objectPath.path();
232 }
else if (objectPath.path() == adapter->path()) {
233 qCWarning(QT_BT_BLUEZ) <<
"DBus Adapter was removed";
234 executeClose(QLowEnergyController::InvalidBluetoothAdapterError);
242 managerBluez =
nullptr;
256 delete deviceMonitor;
257 deviceMonitor =
nullptr;
262 advertiser =
nullptr;
265 if (peripheralApplication)
268 if (peripheralConnectionManager)
274 dbusServices.clear();
278 pendingConnect = disconnectSignalRequired =
false;
287 const QString hostAdapterPath = findAdapterForAddress(localAdapter, &ok);
288 if (!ok || hostAdapterPath.isEmpty()) {
289 qCWarning(QT_BT_BLUEZ) <<
"Cannot find suitable bluetooth adapter";
290 setError(QLowEnergyController::InvalidBluetoothAdapterError);
294 auto manager = std::make_unique<OrgFreedesktopDBusObjectManagerInterface>(
295 QStringLiteral(
"org.bluez"), QStringLiteral(
"/"), QDBusConnection::systemBus());
297 QDBusPendingReply<ManagedObjectList> reply = manager->GetManagedObjects();
298 reply.waitForFinished();
299 if (reply.isError()) {
300 qCWarning(QT_BT_BLUEZ) <<
"Cannot enumerate Bluetooth devices for GATT connect";
301 setError(QLowEnergyController::ConnectionError);
307 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
310 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
311 const QString &iface = jt.key();
312 const QVariantMap &ifaceValues = jt.value();
314 if (iface == QStringLiteral(
"org.bluez.Device1")) {
315 if (remoteDevice.toString() == ifaceValues.value(QStringLiteral(
"Address")).toString())
317 const QVariant adapterForCurrentDevice = ifaceValues.value(QStringLiteral(
"Adapter"));
318 if (qvariant_cast<QDBusObjectPath>(adapterForCurrentDevice).path() == hostAdapterPath) {
319 devicePath = it.key().path();
326 if (!devicePath.isEmpty())
330 if (devicePath.isEmpty()) {
331 qCDebug(QT_BT_BLUEZ) <<
"Cannot find targeted remote device. "
332 "Re-running device discovery might help";
333 setError(QLowEnergyController::UnknownRemoteDeviceError);
337 managerBluez = manager.release();
338 connect(managerBluez, &OrgFreedesktopDBusObjectManagerInterface::InterfacesRemoved,
339 this, &QLowEnergyControllerPrivateBluezDBus::interfacesRemoved);
340 adapter =
new OrgBluezAdapter1Interface(
341 QStringLiteral(
"org.bluez"), hostAdapterPath,
342 QDBusConnection::systemBus(),
this);
343 device =
new OrgBluezDevice1Interface(
344 QStringLiteral(
"org.bluez"), devicePath,
345 QDBusConnection::systemBus(),
this);
346 deviceMonitor =
new OrgFreedesktopDBusPropertiesInterface(
347 QStringLiteral(
"org.bluez"), devicePath,
348 QDBusConnection::systemBus(),
this);
349 connect(deviceMonitor, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
350 this, &QLowEnergyControllerPrivateBluezDBus::devicePropertiesChanged);
355 qCDebug(QT_BT_BLUEZ) <<
"QLowEnergyControllerPrivateBluezDBus::connectToDevice()";
357 connectToDeviceHelper();
359 if (!adapter || !device)
362 if (!adapter->powered()) {
363 qCWarning(QT_BT_BLUEZ) <<
"Error: Local adapter is powered off";
364 setError(QLowEnergyController::ConnectionError);
368 setState(QLowEnergyController::ConnectingState);
372 if (device->connected() && device->servicesResolved()) {
374 disconnectSignalRequired =
true;
376 setState(QLowEnergyController::ConnectedState);
377 Q_Q(QLowEnergyController);
382 pendingConnect =
true;
384 QDBusPendingReply<> reply = device->Connect();
385 QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(reply,
this);
386 connect(watcher, &QDBusPendingCallWatcher::finished,
this,
387 [
this](QDBusPendingCallWatcher* call) {
388 QDBusPendingReply<> reply = *call;
389 if (reply.isError()) {
390 qCDebug(QT_BT_BLUEZ) <<
"BTLE_DBUS::connect() failed"
391 << reply.reply().errorName()
392 << reply.reply().errorMessage();
393 executeClose(QLowEnergyController::UnknownError);
401 if (role == QLowEnergyController::CentralRole) {
405 setState(QLowEnergyController::ClosingState);
407 QDBusPendingReply<> reply = device->Disconnect();
408 QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(reply,
this);
409 connect(watcher, &QDBusPendingCallWatcher::finished,
this,
410 [
this](QDBusPendingCallWatcher* call) {
411 QDBusPendingReply<> reply = *call;
412 if (reply.isError()) {
413 qCDebug(QT_BT_BLUEZ) <<
"BTLE_DBUS::disconnect() failed"
414 << reply.reply().errorName()
415 << reply.reply().errorMessage();
416 executeClose(QLowEnergyController::UnknownError);
418 executeClose(QLowEnergyController::NoError);
423 Q_Q(QLowEnergyController);
426 remoteDevice.clear();
427 const auto emitDisconnected = (state == QLowEnergyController::ConnectedState);
428 setState(QLowEnergyController::UnconnectedState);
429 if (emitDisconnected)
430 emit q->disconnected();
436 QDBusPendingReply<ManagedObjectList> reply = managerBluez->GetManagedObjects();
437 reply.waitForFinished();
438 if (reply.isError()) {
439 qCWarning(QT_BT_BLUEZ) <<
"Cannot discover services";
440 setError(QLowEnergyController::UnknownError);
441 setState(QLowEnergyController::DiscoveredState);
445 Q_Q(QLowEnergyController);
447 auto setupServicePrivate = [&, q](
448 QLowEnergyService::ServiceType type,
const QBluetoothUuid &uuid,
449 const QString &path,
const bool battery1Interface =
false){
450 QSharedPointer<QLowEnergyServicePrivate> priv = QSharedPointer<QLowEnergyServicePrivate>::create();
453 priv->setController(
this);
455 GattService serviceContainer;
456 serviceContainer.servicePath = path;
458 if (battery1Interface) {
459 qCDebug(QT_BT_BLUEZ) <<
"Using Battery1 interface to emulate generic interface";
460 serviceContainer.hasBatteryService =
true;
463 serviceList.insert(priv->uuid, priv);
464 dbusServices.insert(priv->uuid, serviceContainer);
466 emit q->serviceDiscovered(priv->uuid);
470 const QString servicePathPrefix = device->path().append(QStringLiteral(
"/service"));
488 bool gattBatteryService{
false};
489 QString batteryServicePath;
491 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
494 if (!it.key().path().startsWith(device->path()))
497 if (it.key().path() == device->path()) {
499 for (InterfaceList::const_iterator battIter = ifaceList.constBegin(); battIter != ifaceList.constEnd(); ++battIter) {
500 const QString &iface = battIter.key();
501 if (iface == QStringLiteral(
"org.bluez.Battery1")) {
502 qCDebug(QT_BT_BLUEZ) <<
"Dedicated Battery1 service available";
503 batteryServicePath = it.key().path();
505 }
else if (iface == QStringLiteral(
"org.bluez.Device1")) {
506 const auto batteryUuids =
507 battIter.value()[QStringLiteral(
"UUIDs")].toStringList();
508 for (
auto const &uuid : batteryUuids) {
509 if (QBluetoothUuid(uuid) ==
510 QBluetoothUuid::ServiceClassUuid::BatteryService) {
511 qCDebug(QT_BT_BLUEZ) <<
"Battery service listed as available service";
512 batteryServicePath = it.key().path();
521 if (!it.key().path().startsWith(servicePathPrefix))
524 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
525 const QString &iface = jt.key();
527 if (iface == QStringLiteral(
"org.bluez.GattService1")) {
528 QScopedPointer<OrgBluezGattService1Interface> service(
new OrgBluezGattService1Interface(
529 QStringLiteral(
"org.bluez"),it.key().path(),
530 QDBusConnection::systemBus(),
this));
531 if (QBluetoothUuid(service->uUID()) ==
532 QBluetoothUuid::ServiceClassUuid::BatteryService) {
533 qCDebug(QT_BT_BLUEZ) <<
"Using battery service via GattService1 interface";
534 gattBatteryService =
true;
536 setupServicePrivate(service->primary()
537 ? QLowEnergyService::PrimaryService
538 : QLowEnergyService::IncludedService,
539 QBluetoothUuid(service->uUID()), it.key().path());
544 if (!gattBatteryService && !batteryServicePath.isEmpty()) {
545 setupServicePrivate(QLowEnergyService::PrimaryService,
546 QBluetoothUuid::ServiceClassUuid::BatteryService,
547 batteryServicePath,
true);
550 setState(QLowEnergyController::DiscoveredState);
551 emit q->discoveryFinished();
555 GattService &dbusData, QSharedPointer<QLowEnergyServicePrivate> serviceData)
561 auto batteryService = QSharedPointer<OrgBluezBattery1Interface>::create(
562 QStringLiteral(
"org.bluez"), dbusData.servicePath,
563 QDBusConnection::systemBus());
564 dbusData.batteryInterface = batteryService;
566 serviceData->startHandle = runningHandle++;
569 QLowEnergyHandle indexHandle = runningHandle++;
572 charData.valueHandle = runningHandle++;
573 charData.properties.setFlag(QLowEnergyCharacteristic::Read);
574 charData.properties.setFlag(QLowEnergyCharacteristic::Notify);
575 charData.uuid = QBluetoothUuid::CharacteristicType::BatteryLevel;
576 charData.value = QByteArray(1,
char(batteryService->percentage()));
581 QLowEnergyHandle descriptorHandle = runningHandle++;
582 descData.uuid = QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration;
583 descData.value = QByteArray::fromHex(
"0000");
584 charData.descriptorList.insert(descriptorHandle, descData);
586 descriptorHandle = runningHandle++;
587 descData.uuid = QBluetoothUuid::DescriptorType::CharacteristicPresentationFormat;
591 descData.value = QByteArray::fromHex(
"0400ad27011131");
592 charData.descriptorList.insert(descriptorHandle, descData);
594 descriptorHandle = runningHandle++;
595 descData.uuid = QBluetoothUuid::DescriptorType::ReportReference;
596 descData.value = QByteArray::fromHex(
"0401");
597 charData.descriptorList.insert(descriptorHandle, descData);
599 serviceData->characteristicList[indexHandle] = charData;
600 serviceData->endHandle = runningHandle++;
602 serviceData->setState(QLowEnergyService::RemoteServiceDiscovered);
607 const bool emitDisconnect = disconnectSignalRequired;
610 if (newError != QLowEnergyController::NoError)
613 setState(QLowEnergyController::UnconnectedState);
614 if (emitDisconnect) {
615 Q_Q(QLowEnergyController);
616 emit q->disconnected();
621 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
623 if (!serviceList.contains(service) || !dbusServices.contains(service)) {
624 qCWarning(QT_BT_BLUEZ) <<
"Discovery of unknown service" << service.toString()
630 QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(service);
631 serviceData->characteristicList.clear();
633 GattService &dbusData = dbusServices[service];
634 dbusData.characteristics.clear();
636 if (dbusData.hasBatteryService) {
637 qCDebug(QT_BT_BLUEZ) <<
"Triggering Battery1 service discovery on " << dbusData.servicePath;
638 discoverBatteryServiceDetails(dbusData, serviceData);
642 QDBusPendingReply<ManagedObjectList> reply = managerBluez->GetManagedObjects();
643 reply.waitForFinished();
644 if (reply.isError()) {
645 qCWarning(QT_BT_BLUEZ) <<
"Cannot discover services";
646 setError(QLowEnergyController::UnknownError);
647 setState(QLowEnergyController::DiscoveredState);
652 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
654 if (!it.key().path().startsWith(dbusData.servicePath))
657 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
658 const QString &iface = jt.key();
659 if (iface == QStringLiteral(
"org.bluez.GattCharacteristic1")) {
660 auto charInterface = QSharedPointer<OrgBluezGattCharacteristic1Interface>::create(
661 QStringLiteral(
"org.bluez"), it.key().path(),
662 QDBusConnection::systemBus());
663 GattCharacteristic dbusCharData;
664 dbusCharData.characteristic = charInterface;
665 dbusData.characteristics.append(dbusCharData);
666 }
else if (iface == QStringLiteral(
"org.bluez.GattDescriptor1")) {
667 auto descInterface = QSharedPointer<OrgBluezGattDescriptor1Interface>::create(
668 QStringLiteral(
"org.bluez"), it.key().path(),
669 QDBusConnection::systemBus());
671 for (GattCharacteristic &dbusCharData : dbusData.characteristics) {
672 if (!descInterface->path().startsWith(
673 dbusCharData.characteristic->path()))
677 dbusCharData.descriptors.append(descInterface);
683 qCWarning(QT_BT_BLUEZ) <<
"Descriptor discovery error";
689 serviceData->startHandle = runningHandle++;
690 for (GattCharacteristic &dbusChar : dbusData.characteristics) {
691 const QLowEnergyHandle indexHandle = runningHandle++;
692 QLowEnergyServicePrivate::CharData charData;
695 charData.valueHandle = runningHandle++;
696 const QStringList properties = dbusChar.characteristic->flags();
698 for (
const auto &entry : properties) {
699 if (entry == QStringLiteral(
"broadcast"))
700 charData.properties.setFlag(QLowEnergyCharacteristic::Broadcasting,
true);
701 else if (entry == QStringLiteral(
"read"))
702 charData.properties.setFlag(QLowEnergyCharacteristic::Read,
true);
703 else if (entry == QStringLiteral(
"write-without-response"))
704 charData.properties.setFlag(QLowEnergyCharacteristic::WriteNoResponse,
true);
705 else if (entry == QStringLiteral(
"write"))
706 charData.properties.setFlag(QLowEnergyCharacteristic::Write,
true);
707 else if (entry == QStringLiteral(
"notify"))
708 charData.properties.setFlag(QLowEnergyCharacteristic::Notify,
true);
709 else if (entry == QStringLiteral(
"indicate"))
710 charData.properties.setFlag(QLowEnergyCharacteristic::Indicate,
true);
711 else if (entry == QStringLiteral(
"authenticated-signed-writes"))
712 charData.properties.setFlag(QLowEnergyCharacteristic::WriteSigned,
true);
713 else if (entry == QStringLiteral(
"reliable-write"))
714 charData.properties.setFlag(QLowEnergyCharacteristic::ExtendedProperty,
true);
715 else if (entry == QStringLiteral(
"writable-auxiliaries"))
716 charData.properties.setFlag(QLowEnergyCharacteristic::ExtendedProperty,
true);
720 charData.uuid = QBluetoothUuid(dbusChar.characteristic->uUID());
723 if (mode == QLowEnergyService::FullDiscovery
724 && charData.properties.testFlag(QLowEnergyCharacteristic::Read)) {
726 job.flags = GattJob::JobFlags({GattJob::CharRead, GattJob::ServiceDiscovery});
727 job.service = serviceData;
728 job.handle = indexHandle;
733 for (
const auto &descEntry : std::as_const(dbusChar.descriptors)) {
734 const QLowEnergyHandle descriptorHandle = runningHandle++;
735 QLowEnergyServicePrivate::DescData descData;
736 descData.uuid = QBluetoothUuid(descEntry->uUID());
737 charData.descriptorList.insert(descriptorHandle, descData);
742 == QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
743 dbusChar.charMonitor = QSharedPointer<OrgFreedesktopDBusPropertiesInterface>::create(
744 QStringLiteral(
"org.bluez"),
745 dbusChar.characteristic->path(),
746 QDBusConnection::systemBus(),
this);
747 connect(dbusChar.charMonitor.data(), &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
748 this, [
this, indexHandle](
const QString &interface,
const QVariantMap &changedProperties,
749 const QStringList &removedProperties) {
751 characteristicPropertiesChanged(indexHandle, interface,
752 changedProperties, removedProperties);
756 if (mode == QLowEnergyService::FullDiscovery) {
759 job.flags = GattJob::JobFlags({ GattJob::DescRead, GattJob::ServiceDiscovery });
760 job.service = serviceData;
761 job.handle = descriptorHandle;
766 serviceData->characteristicList[indexHandle] = charData;
769 serviceData->endHandle = runningHandle++;
772 if (!jobs.isEmpty()) {
773 GattJob &lastJob = jobs.last();
774 lastJob.flags.setFlag(GattJob::LastServiceDiscovery,
true);
776 serviceData->setState(QLowEnergyService::RemoteServiceDiscovered);
792 if (!jobPending || jobs.isEmpty()) {
794 qCWarning(QT_BT_BLUEZ) <<
"Aborting onCharReadFinished due to disconnect";
795 Q_ASSERT(state == QLowEnergyController::UnconnectedState);
799 const GattJob nextJob = jobs.constFirst();
800 Q_ASSERT(nextJob.flags.testFlag(GattJob::CharRead));
802 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(nextJob.handle);
803 if (service.isNull() || !dbusServices.contains(service->uuid)) {
804 qCWarning(QT_BT_BLUEZ) <<
"onCharReadFinished: Invalid GATT job. Skipping.";
810 service->characteristicList.value(nextJob.handle);
812 bool isServiceDiscovery = nextJob.flags.testFlag(GattJob::ServiceDiscovery);
813 QDBusPendingReply<QByteArray> reply = *call;
814 if (reply.isError()) {
815 qCWarning(QT_BT_BLUEZ) <<
"Cannot initiate reading of" << charData.uuid
816 <<
"of service" << service->uuid
817 << reply.error().name() << reply.error().message();
818 if (!isServiceDiscovery)
819 service->setError(QLowEnergyService::CharacteristicReadError);
821 qCDebug(QT_BT_BLUEZ) <<
"Read Char:" << charData.uuid << reply.value().toHex();
822 if (charData.properties.testFlag(QLowEnergyCharacteristic::Read))
823 updateValueOfCharacteristic(nextJob.handle, reply.value(),
false);
825 if (isServiceDiscovery) {
826 if (nextJob.flags.testFlag(GattJob::LastServiceDiscovery))
827 service->setState(QLowEnergyService::RemoteServiceDiscovered);
829 QLowEnergyCharacteristic ch(service, nextJob.handle);
830 emit service->characteristicRead(ch, reply.value());
840 if (!jobPending || jobs.isEmpty()) {
842 qCWarning(QT_BT_BLUEZ) <<
"Aborting onDescReadFinished due to disconnect";
843 Q_ASSERT(state == QLowEnergyController::UnconnectedState);
847 const GattJob nextJob = jobs.constFirst();
848 Q_ASSERT(nextJob.flags.testFlag(GattJob::DescRead));
850 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(nextJob.handle);
851 if (service.isNull() || !dbusServices.contains(service->uuid)) {
852 qCWarning(QT_BT_BLUEZ) <<
"onDescReadFinished: Invalid GATT job. Skipping.";
858 QLowEnergyCharacteristic ch = characteristicForHandle(nextJob.handle);
860 qCWarning(QT_BT_BLUEZ) <<
"Cannot find char for desc read (onDescReadFinished 1).";
867 service->characteristicList.value(ch.attributeHandle());
869 if (!charData.descriptorList.contains(nextJob.handle)) {
870 qCWarning(QT_BT_BLUEZ) <<
"Cannot find descriptor (onDescReadFinished 2).";
876 bool isServiceDiscovery = nextJob.flags.testFlag(GattJob::ServiceDiscovery);
878 QDBusPendingReply<QByteArray> reply = *call;
879 if (reply.isError()) {
880 qCWarning(QT_BT_BLUEZ) <<
"Cannot read descriptor (onDescReadFinished 3): "
881 << charData.descriptorList[nextJob.handle].uuid
883 << reply.error().name() << reply.error().message();
884 if (!isServiceDiscovery)
885 service->setError(QLowEnergyService::DescriptorReadError);
887 qCDebug(QT_BT_BLUEZ) <<
"Read Desc:" << reply.value();
888 updateValueOfDescriptor(ch.attributeHandle(), nextJob.handle, reply.value(),
false);
890 if (isServiceDiscovery) {
891 if (nextJob.flags.testFlag(GattJob::LastServiceDiscovery))
892 service->setState(QLowEnergyService::RemoteServiceDiscovered);
894 QLowEnergyDescriptor desc(service, ch.attributeHandle(), nextJob.handle);
895 emit service->descriptorRead(desc, reply.value());
905 if (!jobPending || jobs.isEmpty()) {
907 qCWarning(QT_BT_BLUEZ) <<
"Aborting onCharWriteFinished due to disconnect";
908 Q_ASSERT(state == QLowEnergyController::UnconnectedState);
912 const GattJob nextJob = jobs.constFirst();
913 Q_ASSERT(nextJob.flags.testFlag(GattJob::CharWrite));
915 QSharedPointer<QLowEnergyServicePrivate> service = nextJob.service;
916 if (!dbusServices.contains(service->uuid)) {
917 qCWarning(QT_BT_BLUEZ) <<
"onCharWriteFinished: Invalid GATT job. Skipping.";
924 service->characteristicList.value(nextJob.handle);
926 QDBusPendingReply<> reply = *call;
927 if (reply.isError()) {
928 qCWarning(QT_BT_BLUEZ) <<
"Cannot initiate writing of" << charData.uuid
929 <<
"of service" << service->uuid
930 << reply.error().name() << reply.error().message();
931 service->setError(QLowEnergyService::CharacteristicWriteError);
933 if (charData.properties.testFlag(QLowEnergyCharacteristic::Read))
934 updateValueOfCharacteristic(nextJob.handle, nextJob.value,
false);
936 QLowEnergyCharacteristic ch(service, nextJob.handle);
938 if (nextJob.writeMode == QLowEnergyService::WriteWithResponse) {
939 qCDebug(QT_BT_BLUEZ) <<
"Written Char:" << charData.uuid << nextJob.value.toHex();
940 emit service->characteristicWritten(ch, nextJob.value);
950 if (!jobPending || jobs.isEmpty()) {
952 qCWarning(QT_BT_BLUEZ) <<
"Aborting onDescWriteFinished due to disconnect";
953 Q_ASSERT(state == QLowEnergyController::UnconnectedState);
957 const GattJob nextJob = jobs.constFirst();
958 Q_ASSERT(nextJob.flags.testFlag(GattJob::DescWrite));
960 QSharedPointer<QLowEnergyServicePrivate> service = nextJob.service;
961 if (!dbusServices.contains(service->uuid)) {
962 qCWarning(QT_BT_BLUEZ) <<
"onDescWriteFinished: Invalid GATT job. Skipping.";
968 const QLowEnergyCharacteristic associatedChar = characteristicForHandle(nextJob.handle);
969 const QLowEnergyDescriptor descriptor = descriptorForHandle(nextJob.handle);
970 if (!associatedChar.isValid() || !descriptor.isValid()) {
971 qCWarning(QT_BT_BLUEZ) <<
"onDescWriteFinished: Cannot find associated char/desc: "
972 << associatedChar.isValid();
978 QDBusPendingReply<> reply = *call;
979 if (reply.isError()) {
980 qCWarning(QT_BT_BLUEZ) <<
"Cannot initiate writing of" << descriptor.uuid()
981 <<
"of char" << associatedChar.uuid()
982 <<
"of service" << service->uuid
983 << reply.error().name() << reply.error().message();
984 service->setError(QLowEnergyService::DescriptorWriteError);
986 qCDebug(QT_BT_BLUEZ) <<
"Write Desc:" << descriptor.uuid() << nextJob.value.toHex();
987 updateValueOfDescriptor(associatedChar.attributeHandle(), nextJob.handle,
988 nextJob.value,
false);
989 emit service->descriptorWritten(descriptor, nextJob.value);
998 if (jobPending || jobs.isEmpty())
1003 const GattJob nextJob = jobs.constFirst();
1004 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(nextJob.handle);
1005 if (service.isNull() || !dbusServices.contains(service->uuid)) {
1006 qCWarning(QT_BT_BLUEZ) <<
"Invalid GATT job (scheduleNextJob). Skipping.";
1011 const GattService &dbusServiceData = dbusServices[service->uuid];
1013 if (nextJob.flags.testFlag(GattJob::CharRead)) {
1015 if (!service->characteristicList.contains(nextJob.handle)) {
1016 qCWarning(QT_BT_BLUEZ) <<
"Invalid Char handle when reading. Skipping.";
1022 service->characteristicList.value(nextJob.handle);
1023 bool foundChar =
false;
1024 for (
const auto &gattChar : std::as_const(dbusServiceData.characteristics)) {
1025 if (charData.uuid != QBluetoothUuid(gattChar.characteristic->uUID()))
1028 QDBusPendingReply<QByteArray> reply = gattChar.characteristic->ReadValue(QVariantMap());
1029 QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(reply,
this);
1030 connect(watcher, &QDBusPendingCallWatcher::finished,
1031 this, &QLowEnergyControllerPrivateBluezDBus::onCharReadFinished);
1038 qCWarning(QT_BT_BLUEZ) <<
"Cannot find char for reading. Skipping.";
1042 }
else if (nextJob.flags.testFlag(GattJob::CharWrite)) {
1044 if (!service->characteristicList.contains(nextJob.handle)) {
1045 qCWarning(QT_BT_BLUEZ) <<
"Invalid Char handle when writing. Skipping.";
1051 service->characteristicList.value(nextJob.handle);
1052 bool foundChar =
false;
1053 for (
const auto &gattChar : std::as_const(dbusServiceData.characteristics)) {
1054 if (charData.uuid != QBluetoothUuid(gattChar.characteristic->uUID()))
1057 QVariantMap options;
1059 options[QStringLiteral(
"type")] = nextJob.writeMode == QLowEnergyService::WriteWithoutResponse ?
1060 QStringLiteral(
"command") : QStringLiteral(
"request");
1061 QDBusPendingReply<> reply = gattChar.characteristic->WriteValue(nextJob.value, options);
1063 QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(reply,
this);
1064 connect(watcher, &QDBusPendingCallWatcher::finished,
1065 this, &QLowEnergyControllerPrivateBluezDBus::onCharWriteFinished);
1072 qCWarning(QT_BT_BLUEZ) <<
"Cannot find char for writing. Skipping.";
1076 }
else if (nextJob.flags.testFlag(GattJob::DescRead)) {
1078 QLowEnergyCharacteristic ch = characteristicForHandle(nextJob.handle);
1079 if (!ch.isValid()) {
1080 qCWarning(QT_BT_BLUEZ) <<
"Invalid GATT job (scheduleReadDesc 1). Skipping.";
1086 service->characteristicList.value(ch.attributeHandle());
1087 if (!charData.descriptorList.contains(nextJob.handle)) {
1088 qCWarning(QT_BT_BLUEZ) <<
"Invalid GATT job (scheduleReadDesc 2). Skipping.";
1093 const QBluetoothUuid descUuid = charData.descriptorList[nextJob.handle].uuid;
1094 bool foundDesc =
false;
1095 for (
const auto &gattChar : std::as_const(dbusServiceData.characteristics)) {
1096 if (charData.uuid != QBluetoothUuid(gattChar.characteristic->uUID()))
1099 for (
const auto &gattDesc : std::as_const(gattChar.descriptors)) {
1100 if (descUuid != QBluetoothUuid(gattDesc->uUID()))
1103 QDBusPendingReply<QByteArray> reply = gattDesc->ReadValue(QVariantMap());
1104 QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(reply,
this);
1105 connect(watcher, &QDBusPendingCallWatcher::finished,
1106 this, &QLowEnergyControllerPrivateBluezDBus::onDescReadFinished);
1116 qCWarning(QT_BT_BLUEZ) <<
"Cannot find descriptor for reading. Skipping.";
1120 }
else if (nextJob.flags.testFlag(GattJob::DescWrite)) {
1122 const QLowEnergyCharacteristic ch = characteristicForHandle(nextJob.handle);
1123 if (!ch.isValid()) {
1124 qCWarning(QT_BT_BLUEZ) <<
"Invalid GATT job (scheduleWriteDesc 1). Skipping.";
1130 service->characteristicList.value(ch.attributeHandle());
1131 if (!charData.descriptorList.contains(nextJob.handle)) {
1132 qCWarning(QT_BT_BLUEZ) <<
"Invalid GATT job (scheduleWriteDesc 2). Skipping.";
1137 const QBluetoothUuid descUuid = charData.descriptorList[nextJob.handle].uuid;
1138 bool foundDesc =
false;
1139 for (
const auto &gattChar : std::as_const(dbusServiceData.characteristics)) {
1140 if (charData.uuid != QBluetoothUuid(gattChar.characteristic->uUID()))
1143 for (
const auto &gattDesc : std::as_const(gattChar.descriptors)) {
1144 if (descUuid != QBluetoothUuid(gattDesc->uUID()))
1149 if (descUuid == QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1150 const QByteArray value = nextJob.value;
1152 QDBusPendingReply<> reply;
1153 qCDebug(QT_BT_BLUEZ) <<
"Init CCC change to" << value.toHex()
1154 << charData.uuid << service->uuid;
1155 if (value == QByteArray::fromHex(
"0100") || value == QByteArray::fromHex(
"0200"))
1156 reply = gattChar.characteristic->StartNotify();
1158 reply = gattChar.characteristic->StopNotify();
1159 QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(reply,
this);
1160 connect(watcher, &QDBusPendingCallWatcher::finished,
1161 this, &QLowEnergyControllerPrivateBluezDBus::onDescWriteFinished);
1163 QDBusPendingReply<> reply = gattDesc->WriteValue(nextJob.value, QVariantMap());
1164 QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(reply,
this);
1165 connect(watcher, &QDBusPendingCallWatcher::finished,
1166 this, &QLowEnergyControllerPrivateBluezDBus::onDescWriteFinished);
1179 qCWarning(QT_BT_BLUEZ) <<
"Cannot find descriptor for writing. Skipping.";
1184 qCWarning(QT_BT_BLUEZ) <<
"Unknown gatt job type. Skipping.";
1190 const QSharedPointer<QLowEnergyServicePrivate> service,
1191 const QLowEnergyHandle charHandle)
1193 Q_ASSERT(!service.isNull());
1194 if (!service->characteristicList.contains(charHandle)) {
1195 qCWarning(QT_BT_BLUEZ) <<
"Read characteristic does not belong to service"
1201 = service->characteristicList[charHandle];
1202 if (!(charDetails.properties & QLowEnergyCharacteristic::Read)) {
1206 qCWarning(QT_BT_BLUEZ) <<
"Reading non-readable char" << charHandle;
1209 const GattService &gattService = dbusServices[service->uuid];
1210 if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
1212 const QByteArray newValue(1,
char(gattService.batteryInterface->percentage()));
1213 quint16 result = updateValueOfCharacteristic(charHandle, newValue,
false);
1215 QLowEnergyCharacteristic ch(service, charHandle);
1216 emit service->characteristicRead(ch, newValue);
1218 service->setError(QLowEnergyService::CharacteristicReadError);
1224 job.flags = GattJob::JobFlags({GattJob::CharRead});
1225 job.service = service;
1226 job.handle = charHandle;
1233 const QSharedPointer<QLowEnergyServicePrivate> service,
1234 const QLowEnergyHandle charHandle,
1235 const QLowEnergyHandle descriptorHandle)
1237 Q_ASSERT(!service.isNull());
1238 if (!service->characteristicList.contains(charHandle))
1242 = service->characteristicList[charHandle];
1243 if (!charDetails.descriptorList.contains(descriptorHandle))
1246 const GattService &gattService = dbusServices[service->uuid];
1247 if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
1248 auto descriptor = descriptorForHandle(descriptorHandle);
1249 if (descriptor.isValid())
1250 emit service->descriptorRead(descriptor, descriptor.value());
1252 service->setError(QLowEnergyService::DescriptorReadError);
1258 job.flags = GattJob::JobFlags({GattJob::DescRead});
1259 job.service = service;
1260 job.handle = descriptorHandle;
1267 const QSharedPointer<QLowEnergyServicePrivate> service,
1268 const QLowEnergyHandle charHandle,
1269 const QByteArray &newValue,
1270 QLowEnergyService::WriteMode writeMode)
1272 Q_ASSERT(!service.isNull());
1273 if (!service->characteristicList.contains(charHandle)) {
1274 qCWarning(QT_BT_BLUEZ) <<
"Write characteristic does not belong to service"
1279 if (role == QLowEnergyController::CentralRole) {
1280 const GattService &gattService = dbusServices[service->uuid];
1281 if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
1283 service->setError(QLowEnergyService::CharacteristicWriteError);
1289 job.flags = GattJob::JobFlags({GattJob::CharWrite});
1290 job.service = service;
1291 job.handle = charHandle;
1292 job.value = newValue;
1293 job.writeMode = writeMode;
1299 Q_ASSERT(peripheralApplication);
1300 if (!peripheralApplication->localCharacteristicWrite(charHandle, newValue)) {
1301 qCWarning(QT_BT_BLUEZ) <<
"Characteristic write failed"
1302 << characteristicForHandle(charHandle).uuid();
1303 service->setError(QLowEnergyService::CharacteristicWriteError);
1307 charData.value = newValue;
1312 const QSharedPointer<QLowEnergyServicePrivate> service,
1313 const QLowEnergyHandle charHandle,
1314 const QLowEnergyHandle descriptorHandle,
1315 const QByteArray &newValue)
1317 Q_ASSERT(!service.isNull());
1318 if (!service->characteristicList.contains(charHandle))
1321 if (role == QLowEnergyController::CentralRole) {
1322 const GattService &gattService = dbusServices[service->uuid];
1323 if (gattService.hasBatteryService && !gattService.batteryInterface.isNull()) {
1324 auto descriptor = descriptorForHandle(descriptorHandle);
1325 if (!descriptor.isValid())
1328 if (descriptor.uuid() == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration) {
1329 if (newValue == QByteArray::fromHex(
"0000")
1330 || newValue == QByteArray::fromHex(
"0100")
1331 || newValue == QByteArray::fromHex(
"0200")) {
1332 quint16 result = updateValueOfDescriptor(charHandle, descriptorHandle, newValue,
false);
1334 emit service->descriptorWritten(descriptor, newValue);
1336 emit service->setError(QLowEnergyService::DescriptorWriteError);
1340 service->setError(QLowEnergyService::DescriptorWriteError);
1347 job.flags = GattJob::JobFlags({GattJob::DescWrite});
1348 job.service = service;
1349 job.handle = descriptorHandle;
1350 job.value = newValue;
1356 Q_ASSERT(peripheralApplication);
1358 auto desc = descriptorForHandle(descriptorHandle);
1359 if (desc.uuid() == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration) {
1360 qCWarning(QT_BT_BLUEZ) <<
"CCCD write not supported in peripheral role";
1361 service->setError(QLowEnergyService::DescriptorWriteError);
1363 }
else if (!peripheralApplication->localDescriptorWrite(descriptorHandle, newValue)) {
1364 qCWarning(QT_BT_BLUEZ) <<
"Descriptor write failed" << desc.uuid();
1365 service->setError(QLowEnergyService::DescriptorWriteError);
1368 service->characteristicList[charHandle].descriptorList[descriptorHandle].value = newValue;
1373 const QLowEnergyAdvertisingParameters ¶ms,
1374 const QLowEnergyAdvertisingData &advertisingData,
1375 const QLowEnergyAdvertisingData &scanResponseData)
1377 error = QLowEnergyController::NoError;
1378 errorString.clear();
1380 Q_ASSERT(peripheralApplication);
1381 Q_ASSERT(!adapterPathWithPeripheralSupport.isEmpty());
1387 advertiser =
nullptr;
1389 advertiser =
new QLeDBusAdvertiser(params, advertisingData, scanResponseData,
1390 adapterPathWithPeripheralSupport,
this);
1391 connect(advertiser, &QLeDBusAdvertiser::errorOccurred,
1394 setState(QLowEnergyController::AdvertisingState);
1403 advertiser->startAdvertising();
1409 setState(QLowEnergyController::UnconnectedState);
1411 advertiser->stopAdvertising();
1413 advertiser =
nullptr;
1422 if (advertiser && state == QLowEnergyController::AdvertisingState)
1423 advertiser->startAdvertising();
1429 QLowEnergyHandle handle,
const QByteArray& value)
1431 const auto characteristic = characteristicForHandle(handle);
1432 if (characteristic.d_ptr
1433 && updateValueOfCharacteristic(handle, value,
false) == value.size()) {
1434 emit characteristic.d_ptr->characteristicChanged(characteristic, value);
1436 qCWarning(QT_BT_BLUEZ) <<
"Remote characteristic write failed";
1441 QLowEnergyHandle characteristicHandle,
1442 QLowEnergyHandle descriptorHandle,
1443 const QByteArray& value)
1445 const auto descriptor = descriptorForHandle(descriptorHandle);
1446 if (descriptor.d_ptr && updateValueOfDescriptor(
1447 characteristicHandle, descriptorHandle, value,
false) == value.size()) {
1448 emit descriptor.d_ptr->descriptorWritten(descriptor, value);
1450 qCWarning(QT_BT_BLUEZ) <<
"Remote descriptor write failed";
1455 const QBluetoothAddress& address,
1456 const QString& name,
1459 remoteDevice = address;
1466 Q_ASSERT(peripheralApplication);
1467 qCWarning(QT_BT_BLUEZ) <<
"An advertising error occurred";
1468 setError(QLowEnergyController::AdvertisingError);
1469 setState(QLowEnergyController::UnconnectedState);
1475 qCWarning(QT_BT_BLUEZ) <<
"A Bluez peripheral application error occurred";
1476 setError(QLowEnergyController::UnknownError);
1477 setState(QLowEnergyController::UnconnectedState);
1482 Q_Q(QLowEnergyController);
1483 qCDebug(QT_BT_BLUEZ) <<
"Peripheral application connected change to:" << connected;
1485 setState(QLowEnergyController::ConnectedState);
1488 remoteDevice.clear();
1489 setState(QLowEnergyController::UnconnectedState);
1490 emit q->disconnected();
1495 const QLowEnergyConnectionParameters & )
1497 qCWarning(QT_BT_BLUEZ) <<
"Connection update requests not supported on Bluez DBus";
1501 const QLowEnergyServiceData &serviceData,
1502 QLowEnergyHandle startHandle)
1504 Q_ASSERT(peripheralApplication);
1505 QSharedPointer<QLowEnergyServicePrivate> servicePrivate = serviceForHandle(startHandle);
1506 if (servicePrivate.isNull())
1508 peripheralApplication->addService(serviceData, servicePrivate, startHandle);
1519#include "moc_qlowenergycontroller_bluezdbus_p.cpp"
QMap< QDBusObjectPath, InterfaceList > ManagedObjectList
QMap< QString, QVariantMap > InterfaceList
void requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) override
QLowEnergyControllerPrivateBluezDBus(const QString &adapterPathWithPeripheralSupport={})
void startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData) override
void discoverServiceDetails(const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode) override
void discoverServices() override
~QLowEnergyControllerPrivateBluezDBus() override
void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override
void writeCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QByteArray &newValue, QLowEnergyService::WriteMode writeMode) override
void writeDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, const QByteArray &newValue) override
void disconnectFromDevice() override
void readDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle) override
void readCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle) override
void stopAdvertising() override
void connectToDevice() override
void invalidateServices()
void unregisterApplication()
void registerApplication()
bool registrationNeeded()
Combined button and popup list for selecting options.