Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
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
7#include "gattmanager1_p.h"
8
10
12
13using namespace Qt::StringLiterals;
14
15static constexpr QLatin1String appObjectPathTemplate{"/qt/btle/application/%1%2/%3"};
16
18 QObject* parent)
19 : QObject(parent),
20 m_objectPath(QString(appObjectPathTemplate).
21 arg(sanitizeNameForDBus(QCoreApplication::applicationName())).
22 arg(QCoreApplication::applicationPid()).
23 arg(QRandomGenerator::global()->generate()))
24{
25 m_objectManager = new OrgFreedesktopDBusObjectManagerAdaptor(this);
26 m_gattManager = new OrgBluezGattManager1Interface("org.bluez"_L1, hostAdapterPath,
28}
29
34
36{
37 if (m_applicationRegistered) {
38 // Can happen eg. if advertisement is start-stop-started
39 qCDebug(QT_BT_BLUEZ) << "Bluez peripheral application already registered";
40 return;
41 }
42
43 if (m_services.isEmpty()) {
44 // Registering the application to bluez without services would fail
45 qCDebug(QT_BT_BLUEZ) << "No services, omiting Bluez peripheral application registration";
46 return;
47 }
48
49 qCDebug(QT_BT_BLUEZ) << "Registering bluez peripheral application:" << m_objectPath;
50
51 // Register this application object on DBus
52 if (!QDBusConnection::systemBus().registerObject(m_objectPath, m_objectManager,
54 qCWarning(QT_BT_BLUEZ) << "Peripheral application object registration failed";
56 return;
57 }
58
59 // Register the service objects on DBus
60 registerServices();
61
62 // Register the gatt application to Bluez. After successful registration Bluez
63 // is aware of this peripheral application and will inquiry which services this application
64 // provides, see GetManagedObjects()
65 auto reply = m_gattManager->RegisterApplication(QDBusObjectPath(m_objectPath), {});
70 if (reply.isError()) {
71 qCWarning(QT_BT_BLUEZ) << "Application registration failed" << reply.error();
72 QDBusConnection::systemBus().unregisterObject(m_objectPath);
73 emit errorOccurred();
74 } else {
75 qCDebug(QT_BT_BLUEZ) << "Peripheral application registered as" << m_objectPath;
76 m_applicationRegistered = true;
77 emit registered();
78 }
79 watcher->deleteLater();
80 });
81}
82
84{
85 if (!m_applicationRegistered)
86 return;
87 m_applicationRegistered = false;
88 auto reply = m_gattManager->UnregisterApplication(QDBusObjectPath(m_objectPath));
89 reply.waitForFinished();
90 if (reply.isError())
91 qCWarning(QT_BT_BLUEZ) << "Error in unregistering peripheral application";
92 else
93 qCDebug(QT_BT_BLUEZ) << "Peripheral application unregistered successfully";
94 QDBusConnection::systemBus().unregisterObject(m_objectPath);
95 unregisterServices();
96
97 qCDebug(QT_BT_BLUEZ) << "Unregistered Bluez peripheral application on DBus:" << m_objectPath;
98}
99
100void QtBluezPeripheralApplication::registerServices()
101{
102 // Register the service objects on DBus
103 for (const auto service: std::as_const(m_services))
104 service->registerObject();
105 for (const auto& characteristic : std::as_const(m_characteristics))
106 characteristic->registerObject();
107 for (const auto& descriptor : std::as_const(m_descriptors))
108 descriptor->registerObject();
109}
110
111void QtBluezPeripheralApplication::unregisterServices()
112{
113 // Unregister the service objects from DBus
114 for (const auto service: std::as_const(m_services))
115 service->unregisterObject();
116 for (const auto& characteristic : std::as_const(m_characteristics))
117 characteristic->unregisterObject();
118 for (const auto& descriptor : std::as_const(m_descriptors))
119 descriptor->unregisterObject();
120}
121
123{
125
126 qDeleteAll(m_services);
127 m_services.clear();
128 qDeleteAll(m_descriptors);
129 m_descriptors.clear();
130 qDeleteAll(m_characteristics);
131 m_characteristics.clear();
132}
133
135 QSharedPointer<QLowEnergyServicePrivate> servicePrivate,
136 QLowEnergyHandle serviceHandle)
137{
138 if (m_applicationRegistered) {
139 qCWarning(QT_BT_BLUEZ) << "Adding services to a registered application is not supported "
140 "on Bluez DBus. Add services only before first advertisement or "
141 "after disconnection";
142 return;
143 }
144
145 // The ordinal numbers in the below object creation are used to create paths such as:
146 // ../service0/char0/desc0
147 // ../service0/char1/desc0
148 // ../service1/char0/desc0
149 // ../service1/char0/desc1
150 // For the Service object itself the ordinal number is the size of the service container
152 serviceData, m_objectPath, m_services.size(), serviceHandle, this);
153 m_services.insert(serviceHandle, service);
154
155 // Add included services
156 for (const auto includedService : serviceData.includedServices()) {
157 // As per Qt documentation the included service must have been added earlier
158 for (const auto s : std::as_const(m_services)) {
159 if (QBluetoothUuid(s->uuid) == includedService->serviceUuid()) {
160 service->addIncludedService(s->objectPath);
161 }
162 }
163 }
164
165 // Set characteristics and their descriptors
166 quint16 characteristicOrdinal{0};
167 for (const auto& characteristicData : serviceData.characteristics()) {
168 auto characteristicHandle = handleForCharacteristic(
169 characteristicData.uuid(), servicePrivate);
170 QtBluezPeripheralCharacteristic* characteristic =
171 new QtBluezPeripheralCharacteristic(characteristicData,
172 service->objectPath, characteristicOrdinal++,
173 characteristicHandle, this);
174 m_characteristics.insert(characteristicHandle, characteristic);
179
180 quint16 descriptorOrdinal{0};
181 for (const auto& descriptorData : characteristicData.descriptors()) {
182 // With bluez we don't use the CCCD user has provided, because Bluez
183 // generates it if 'notify/indicate' flag is set. Similarly the extended properties
184 // descriptor is generated by Bluez if the related flags are set. Using the application
185 // provided descriptors would result in duplicate descriptors.
186 if (descriptorData.uuid()
188 || descriptorData.uuid()
190 continue;
191 }
192 auto descriptorHandle = handleForDescriptor(descriptorData.uuid(),
193 servicePrivate,
194 characteristicHandle);
195 QtBluezPeripheralDescriptor* descriptor =
196 new QtBluezPeripheralDescriptor(descriptorData,
197 characteristic->objectPath, descriptorOrdinal++,
198 descriptorHandle, characteristicHandle, this);
203 m_descriptors.insert(descriptorHandle, descriptor);
204 }
205 }
206}
207
208// This function is called when characteristic is written to from Qt API
210 const QByteArray& value)
211{
212 auto characteristic = m_characteristics.value(handle);
213 if (!characteristic) {
214 qCWarning(QT_BT_BLUEZ) << "DBus characteristic not found for write";
215 return false;
216 }
217 return characteristic->localValueUpdate(value);
218}
219
220// This function is called when characteristic is written to from Qt API
222 const QByteArray& value)
223{
224 auto descriptor = m_descriptors.value(handle);
225 if (!descriptor) {
226 qCWarning(QT_BT_BLUEZ) << "DBus descriptor not found for write";
227 return false;
228 }
229 return descriptor->localValueUpdate(value);
230}
231
233{
234 return !m_applicationRegistered && !m_services.isEmpty();
235}
236
237// org.freedesktop.DBus.ObjectManager
238// This is called by Bluez when we register the application
240{
241 ManagedObjectList managedObjects;
242 for (const auto service: std::as_const(m_services))
243 managedObjects.insert(QDBusObjectPath(service->objectPath), service->properties());
244 for (const auto& charac : std::as_const(m_characteristics))
245 managedObjects.insert(QDBusObjectPath(charac->objectPath), charac->properties());
246 for (const auto& descriptor : std::as_const(m_descriptors))
247 managedObjects.insert(QDBusObjectPath(descriptor->objectPath), descriptor->properties());
248
249 return managedObjects;
250}
251
252// Returns the Qt-internal handle for the characteristic
253QLowEnergyHandle QtBluezPeripheralApplication::handleForCharacteristic(QBluetoothUuid uuid,
254 QSharedPointer<QLowEnergyServicePrivate> service)
255{
256 const auto handles = service->characteristicList.keys();
257 for (const auto handle : handles) {
258 if (uuid == service->characteristicList[handle].uuid)
259 return handle;
260 }
261 return 0;
262}
263
264// Returns the Qt-internal handle for the descriptor
265QLowEnergyHandle QtBluezPeripheralApplication::handleForDescriptor(QBluetoothUuid uuid,
266 QSharedPointer<QLowEnergyServicePrivate> service,
267 QLowEnergyHandle characteristicHandle)
268{
269 const auto characteristicData = service->characteristicList[characteristicHandle];
270 const auto handles = characteristicData.descriptorList.keys();
271 for (const auto handle : handles) {
272 if (uuid == characteristicData.descriptorList[handle].uuid)
273 return handle;
274 }
275 return 0;
276}
277
279
280#include "moc_bluezperipheralapplication_p.cpp"
QString sanitizeNameForDBus(const QString &text)
QMap< QDBusObjectPath, InterfaceList > ManagedObjectList
static constexpr QLatin1String appObjectPathTemplate
QDBusPendingReply RegisterApplication(const QDBusObjectPath &application, const QVariantMap &options)
QDBusPendingReply UnregisterApplication(const QDBusObjectPath &application)
\inmodule QtBluetooth
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
static QDBusConnection systemBus()
Returns a QDBusConnection object opened with the system bus.
\inmodule QtDBus
void finished(QDBusPendingCallWatcher *self=nullptr)
This signal is emitted when the pending call has finished and its reply is available.
The QLowEnergyServiceData class is used to set up GATT service data. \inmodule QtBluetooth.
QList< QLowEnergyCharacteristicData > characteristics() const
Returns the list of characteristics.
QList< QLowEnergyService * > includedServices() const
Returns the list of included services.
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:357
void clear()
Definition qmap.h:289
bool isEmpty() const
Definition qmap.h:269
size_type size() const
Definition qmap.h:267
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore \reentrant
Definition qrandom.h:21
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
Q_INVOKABLE ManagedObjectList GetManagedObjects()
bool localDescriptorWrite(QLowEnergyHandle handle, const QByteArray &value)
void remoteDeviceAccessEvent(const QString &remoteDeviceObjectPath, quint16 mtu)
QtBluezPeripheralApplication(const QString &localAdapterPath, QObject *parent=nullptr)
void addService(const QLowEnergyServiceData &serviceData, QSharedPointer< QLowEnergyServicePrivate > servicePrivate, QLowEnergyHandle serviceHandle)
void characteristicValueUpdatedByRemote(QLowEnergyHandle handle, const QByteArray &value)
bool localCharacteristicWrite(QLowEnergyHandle handle, const QByteArray &value)
void descriptorValueUpdatedByRemote(QLowEnergyHandle characteristicHandle, QLowEnergyHandle descriptorHandle, const QByteArray &value)
void valueUpdatedByRemote(QLowEnergyHandle handle, const QByteArray &value)
void valueUpdatedByRemote(QLowEnergyHandle characteristicHandle, QLowEnergyHandle descriptorHandle, const QByteArray &value)
void remoteDeviceAccessEvent(const QString &remoteDeviceObjectPath, quint16 mtu)
qDeleteAll(list.begin(), list.end())
Combined button and popup list for selecting options.
Q_CORE_EXPORT QtJniTypes::Service service()
quint16 QLowEnergyHandle
Definition qbluetooth.h:42
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLuint64 GLenum void * handle
GLdouble s
[6]
Definition qopenglext.h:235
SSL_CTX int void * arg
#define emit
unsigned short quint16
Definition qtypes.h:48
QFutureWatcher< int > watcher
QNetworkReply * reply
QJSValue global