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 const auto includedServices = serviceData.includedServices();
158 for (const auto includedService : includedServices) {
159 // As per Qt documentation the included service must have been added earlier
160 for (const auto s : std::as_const(m_services)) {
161 if (QBluetoothUuid(s->uuid) == includedService->serviceUuid()) {
162 service->addIncludedService(s->objectPath);
163 }
164 }
165 }
166
167 // Set characteristics and their descriptors
168 quint16 characteristicOrdinal{0};
169 const auto characteristics = serviceData.characteristics();
170 for (const auto &characteristicData : characteristics) {
171 auto characteristicHandle = handleForCharacteristic(
172 characteristicData.uuid(), servicePrivate);
173 QtBluezPeripheralCharacteristic* characteristic =
174 new QtBluezPeripheralCharacteristic(characteristicData,
175 service->objectPath, characteristicOrdinal++,
176 characteristicHandle, this);
177 m_characteristics.insert(characteristicHandle, characteristic);
178 QObject::connect(characteristic, &QtBluezPeripheralCharacteristic::valueUpdatedByRemote,
179 this, &QtBluezPeripheralApplication::characteristicValueUpdatedByRemote);
180 QObject::connect(characteristic, &QtBluezPeripheralCharacteristic::remoteDeviceAccessEvent,
181 this, &QtBluezPeripheralApplication::remoteDeviceAccessEvent);
182
183 quint16 descriptorOrdinal{0};
184 const auto descriptors = characteristicData.descriptors();
185 for (const auto &descriptorData : descriptors) {
186 // With bluez we don't use the CCCD user has provided, because Bluez
187 // generates it if 'notify/indicate' flag is set. Similarly the extended properties
188 // descriptor is generated by Bluez if the related flags are set. Using the application
189 // provided descriptors would result in duplicate descriptors.
190 if (descriptorData.uuid()
191 == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration
192 || descriptorData.uuid()
193 == QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties) {
194 continue;
195 }
196 auto descriptorHandle = handleForDescriptor(descriptorData.uuid(),
197 servicePrivate,
198 characteristicHandle);
199 QtBluezPeripheralDescriptor* descriptor =
200 new QtBluezPeripheralDescriptor(descriptorData,
201 characteristic->objectPath, descriptorOrdinal++,
202 descriptorHandle, characteristicHandle, this);
203 QObject::connect(descriptor, &QtBluezPeripheralDescriptor::valueUpdatedByRemote,
204 this, &QtBluezPeripheralApplication::descriptorValueUpdatedByRemote);
205 QObject::connect(descriptor, &QtBluezPeripheralCharacteristic::remoteDeviceAccessEvent,
206 this, &QtBluezPeripheralApplication::remoteDeviceAccessEvent);
207 m_descriptors.insert(descriptorHandle, descriptor);
208 }
209 }
210}
211
212// This function is called when characteristic is written to from Qt API
214 const QByteArray& value)
215{
216 auto characteristic = m_characteristics.value(handle);
217 if (!characteristic) {
218 qCWarning(QT_BT_BLUEZ) << "DBus characteristic not found for write";
219 return false;
220 }
221 return characteristic->localValueUpdate(value);
222}
223
224// This function is called when characteristic is written to from Qt API
226 const QByteArray& value)
227{
228 auto descriptor = m_descriptors.value(handle);
229 if (!descriptor) {
230 qCWarning(QT_BT_BLUEZ) << "DBus descriptor not found for write";
231 return false;
232 }
233 return descriptor->localValueUpdate(value);
234}
235
237{
238 return !m_applicationRegistered && !m_services.isEmpty();
239}
240
241// org.freedesktop.DBus.ObjectManager
242// This is called by Bluez when we register the application
244{
245 ManagedObjectList managedObjects;
246 for (const auto service: std::as_const(m_services))
247 managedObjects.insert(QDBusObjectPath(service->objectPath), service->properties());
248 for (const auto& charac : std::as_const(m_characteristics))
249 managedObjects.insert(QDBusObjectPath(charac->objectPath), charac->properties());
250 for (const auto& descriptor : std::as_const(m_descriptors))
251 managedObjects.insert(QDBusObjectPath(descriptor->objectPath), descriptor->properties());
252
253 return managedObjects;
254}
255
256// Returns the Qt-internal handle for the characteristic
257QLowEnergyHandle QtBluezPeripheralApplication::handleForCharacteristic(QBluetoothUuid uuid,
258 QSharedPointer<QLowEnergyServicePrivate> service)
259{
260 const auto handles = service->characteristicList.keys();
261 for (const auto handle : handles) {
262 if (uuid == service->characteristicList[handle].uuid)
263 return handle;
264 }
265 return 0;
266}
267
268// Returns the Qt-internal handle for the descriptor
269QLowEnergyHandle QtBluezPeripheralApplication::handleForDescriptor(QBluetoothUuid uuid,
270 QSharedPointer<QLowEnergyServicePrivate> service,
271 QLowEnergyHandle characteristicHandle)
272{
273 const auto characteristicData = service->characteristicList[characteristicHandle];
274 const auto handles = characteristicData.descriptorList.keys();
275 for (const auto handle : handles) {
276 if (uuid == characteristicData.descriptorList[handle].uuid)
277 return handle;
278 }
279 return 0;
280}
281
282QT_END_NAMESPACE
283
284#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)