Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
bluezperipheralapplication.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
8
9QT_BEGIN_NAMESPACE
10
11Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
12
13using namespace Qt::StringLiterals;
14using namespace QtBluetoothPrivate; // for D-Bus wrappers
15
16static constexpr QLatin1String appObjectPathTemplate{"/qt/btle/application/%1%2/%3"};
17
18QtBluezPeripheralApplication::QtBluezPeripheralApplication(const QString& hostAdapterPath,
19 QObject* parent)
20 : QObject(parent),
21 m_objectPath(QString(appObjectPathTemplate).
22 arg(sanitizeNameForDBus(QCoreApplication::applicationName())).
23 arg(QCoreApplication::applicationPid()).
24 arg(QRandomGenerator::global()->generate()))
25{
26 m_objectManager = new OrgFreedesktopDBusObjectManagerAdaptor(this);
27 m_gattManager = new OrgBluezGattManager1Interface("org.bluez"_L1, hostAdapterPath,
28 QDBusConnection::systemBus(), this);
29}
30
35
37{
38 if (m_applicationRegistered) {
39 // Can happen eg. if advertisement is start-stop-started
40 qCDebug(QT_BT_BLUEZ) << "Bluez peripheral application already registered";
41 return;
42 }
43
44 if (m_services.isEmpty()) {
45 // Registering the application to bluez without services would fail
46 qCDebug(QT_BT_BLUEZ) << "No services, omiting Bluez peripheral application registration";
47 return;
48 }
49
50 qCDebug(QT_BT_BLUEZ) << "Registering bluez peripheral application:" << m_objectPath;
51
52 // Register this application object on DBus
53 if (!QDBusConnection::systemBus().registerObject(m_objectPath, m_objectManager,
54 QDBusConnection::ExportAllContents)) {
55 qCWarning(QT_BT_BLUEZ) << "Peripheral application object registration failed";
56 emit errorOccurred();
57 return;
58 }
59
60 // Register the service objects on DBus
61 registerServices();
62
63 // Register the gatt application to Bluez. After successful registration Bluez
64 // is aware of this peripheral application and will inquiry which services this application
65 // provides, see GetManagedObjects()
66 auto reply = m_gattManager->RegisterApplication(QDBusObjectPath(m_objectPath), {});
67 QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
68 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this,
69 [this](QDBusPendingCallWatcher* watcher) {
70 QDBusPendingReply<> reply = *watcher;
71 if (reply.isError()) {
72 qCWarning(QT_BT_BLUEZ) << "Application registration failed" << reply.error();
73 QDBusConnection::systemBus().unregisterObject(m_objectPath);
74 emit errorOccurred();
75 } else {
76 qCDebug(QT_BT_BLUEZ) << "Peripheral application registered as" << m_objectPath;
77 m_applicationRegistered = true;
78 emit registered();
79 }
80 watcher->deleteLater();
81 });
82}
83
85{
86 if (!m_applicationRegistered)
87 return;
88 m_applicationRegistered = false;
89 auto reply = m_gattManager->UnregisterApplication(QDBusObjectPath(m_objectPath));
90 reply.waitForFinished();
91 if (reply.isError())
92 qCWarning(QT_BT_BLUEZ) << "Error in unregistering peripheral application";
93 else
94 qCDebug(QT_BT_BLUEZ) << "Peripheral application unregistered successfully";
95 QDBusConnection::systemBus().unregisterObject(m_objectPath);
96 unregisterServices();
97
98 qCDebug(QT_BT_BLUEZ) << "Unregistered Bluez peripheral application on DBus:" << m_objectPath;
99}
100
101void QtBluezPeripheralApplication::registerServices()
102{
103 // Register the service objects on DBus
104 for (const auto service: std::as_const(m_services))
105 service->registerObject();
106 for (const auto& characteristic : std::as_const(m_characteristics))
107 characteristic->registerObject();
108 for (const auto& descriptor : std::as_const(m_descriptors))
109 descriptor->registerObject();
110}
111
112void QtBluezPeripheralApplication::unregisterServices()
113{
114 // Unregister the service objects from DBus
115 for (const auto service: std::as_const(m_services))
116 service->unregisterObject();
117 for (const auto& characteristic : std::as_const(m_characteristics))
118 characteristic->unregisterObject();
119 for (const auto& descriptor : std::as_const(m_descriptors))
120 descriptor->unregisterObject();
121}
122
124{
126
127 qDeleteAll(m_services);
128 m_services.clear();
129 qDeleteAll(m_descriptors);
130 m_descriptors.clear();
131 qDeleteAll(m_characteristics);
132 m_characteristics.clear();
133}
134
135void QtBluezPeripheralApplication::addService(const QLowEnergyServiceData &serviceData,
136 QSharedPointer<QLowEnergyServicePrivate> servicePrivate,
137 QLowEnergyHandle serviceHandle)
138{
139 if (m_applicationRegistered) {
140 qCWarning(QT_BT_BLUEZ) << "Adding services to a registered application is not supported "
141 "on Bluez DBus. Add services only before first advertisement or "
142 "after disconnection";
143 return;
144 }
145
146 // The ordinal numbers in the below object creation are used to create paths such as:
147 // ../service0/char0/desc0
148 // ../service0/char1/desc0
149 // ../service1/char0/desc0
150 // ../service1/char0/desc1
151 // For the Service object itself the ordinal number is the size of the service container
152 QtBluezPeripheralService* service = new QtBluezPeripheralService(
153 serviceData, m_objectPath, m_services.size(), serviceHandle, this);
154 m_services.insert(serviceHandle, service);
155
156 // Add included services
157 for (const auto includedService : serviceData.includedServices()) {
158 // As per Qt documentation the included service must have been added earlier
159 for (const auto s : std::as_const(m_services)) {
160 if (QBluetoothUuid(s->uuid) == includedService->serviceUuid()) {
161 service->addIncludedService(s->objectPath);
162 }
163 }
164 }
165
166 // Set characteristics and their descriptors
167 quint16 characteristicOrdinal{0};
168 for (const auto& characteristicData : serviceData.characteristics()) {
169 auto characteristicHandle = handleForCharacteristic(
170 characteristicData.uuid(), servicePrivate);
171 QtBluezPeripheralCharacteristic* characteristic =
172 new QtBluezPeripheralCharacteristic(characteristicData,
173 service->objectPath, characteristicOrdinal++,
174 characteristicHandle, this);
175 m_characteristics.insert(characteristicHandle, characteristic);
176 QObject::connect(characteristic, &QtBluezPeripheralCharacteristic::valueUpdatedByRemote,
177 this, &QtBluezPeripheralApplication::characteristicValueUpdatedByRemote);
178 QObject::connect(characteristic, &QtBluezPeripheralCharacteristic::remoteDeviceAccessEvent,
179 this, &QtBluezPeripheralApplication::remoteDeviceAccessEvent);
180
181 quint16 descriptorOrdinal{0};
182 for (const auto& descriptorData : characteristicData.descriptors()) {
183 // With bluez we don't use the CCCD user has provided, because Bluez
184 // generates it if 'notify/indicate' flag is set. Similarly the extended properties
185 // descriptor is generated by Bluez if the related flags are set. Using the application
186 // provided descriptors would result in duplicate descriptors.
187 if (descriptorData.uuid()
188 == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration
189 || descriptorData.uuid()
190 == QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties) {
191 continue;
192 }
193 auto descriptorHandle = handleForDescriptor(descriptorData.uuid(),
194 servicePrivate,
195 characteristicHandle);
196 QtBluezPeripheralDescriptor* descriptor =
197 new QtBluezPeripheralDescriptor(descriptorData,
198 characteristic->objectPath, descriptorOrdinal++,
199 descriptorHandle, characteristicHandle, this);
200 QObject::connect(descriptor, &QtBluezPeripheralDescriptor::valueUpdatedByRemote,
201 this, &QtBluezPeripheralApplication::descriptorValueUpdatedByRemote);
202 QObject::connect(descriptor, &QtBluezPeripheralCharacteristic::remoteDeviceAccessEvent,
203 this, &QtBluezPeripheralApplication::remoteDeviceAccessEvent);
204 m_descriptors.insert(descriptorHandle, descriptor);
205 }
206 }
207}
208
209// This function is called when characteristic is written to from Qt API
211 const QByteArray& value)
212{
213 auto characteristic = m_characteristics.value(handle);
214 if (!characteristic) {
215 qCWarning(QT_BT_BLUEZ) << "DBus characteristic not found for write";
216 return false;
217 }
218 return characteristic->localValueUpdate(value);
219}
220
221// This function is called when characteristic is written to from Qt API
223 const QByteArray& value)
224{
225 auto descriptor = m_descriptors.value(handle);
226 if (!descriptor) {
227 qCWarning(QT_BT_BLUEZ) << "DBus descriptor not found for write";
228 return false;
229 }
230 return descriptor->localValueUpdate(value);
231}
232
234{
235 return !m_applicationRegistered && !m_services.isEmpty();
236}
237
238// org.freedesktop.DBus.ObjectManager
239// This is called by Bluez when we register the application
241{
242 ManagedObjectList managedObjects;
243 for (const auto service: std::as_const(m_services))
244 managedObjects.insert(QDBusObjectPath(service->objectPath), service->properties());
245 for (const auto& charac : std::as_const(m_characteristics))
246 managedObjects.insert(QDBusObjectPath(charac->objectPath), charac->properties());
247 for (const auto& descriptor : std::as_const(m_descriptors))
248 managedObjects.insert(QDBusObjectPath(descriptor->objectPath), descriptor->properties());
249
250 return managedObjects;
251}
252
253// Returns the Qt-internal handle for the characteristic
254QLowEnergyHandle QtBluezPeripheralApplication::handleForCharacteristic(QBluetoothUuid uuid,
255 QSharedPointer<QLowEnergyServicePrivate> service)
256{
257 const auto handles = service->characteristicList.keys();
258 for (const auto handle : handles) {
259 if (uuid == service->characteristicList[handle].uuid)
260 return handle;
261 }
262 return 0;
263}
264
265// Returns the Qt-internal handle for the descriptor
266QLowEnergyHandle QtBluezPeripheralApplication::handleForDescriptor(QBluetoothUuid uuid,
267 QSharedPointer<QLowEnergyServicePrivate> service,
268 QLowEnergyHandle characteristicHandle)
269{
270 const auto characteristicData = service->characteristicList[characteristicHandle];
271 const auto handles = characteristicData.descriptorList.keys();
272 for (const auto handle : handles) {
273 if (uuid == characteristicData.descriptorList[handle].uuid)
274 return handle;
275 }
276 return 0;
277}
278
279QT_END_NAMESPACE
280
281#include "moc_bluezperipheralapplication_p.cpp"
QMap< QDBusObjectPath, InterfaceList > ManagedObjectList
static constexpr QLatin1String appObjectPathTemplate
bool localDescriptorWrite(QLowEnergyHandle handle, const QByteArray &value)
void addService(const QLowEnergyServiceData &serviceData, QSharedPointer< QLowEnergyServicePrivate > servicePrivate, QLowEnergyHandle serviceHandle)
bool localCharacteristicWrite(QLowEnergyHandle handle, const QByteArray &value)