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
qleadvertiser_bluezdbus.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
9#include <QtCore/QtMinMax>
10#include <QtCore/QLoggingCategory>
11
13
15
16using namespace Qt::StringLiterals;
17
18// The advertisement dbus object path is freely definable, use as prefix
19static constexpr auto advObjectPathTemplate{"/qt/btle/advertisement/%1%2/%3"_L1};
20static constexpr auto bluezService{"org.bluez"_L1};
21static constexpr auto bluezErrorFailed{"org.bluez.Error.Failed"_L1};
22
23// From bluez API documentation
24static constexpr auto advDataTXPower{"tx-power"_L1};
25static constexpr auto advDataTypePeripheral{"peripheral"_L1};
26static constexpr auto advDataTypeBroadcast{"broadcast"_L1};
27static constexpr quint16 advDataMinIntervalMs{20};
28static constexpr quint16 advDataMaxIntervalMs{10485};
29
30
32 const QLowEnergyAdvertisingData &advertisingData,
33 const QLowEnergyAdvertisingData &scanResponseData,
34 const QString &hostAdapterPath,
35 QObject* parent)
36 : QObject(parent),
37 m_advParams(params),
38 m_advData(advertisingData),
39 m_advObjectPath(QString(advObjectPathTemplate).
40 arg(sanitizeNameForDBus(QCoreApplication::applicationName())).
41 arg(QCoreApplication::applicationPid()).
42 arg(QRandomGenerator::global()->generate())),
43 m_advDataDBus(new OrgBluezLEAdvertisement1Adaptor(this)),
44 m_advManager(new OrgBluezLEAdvertisingManager1Interface(bluezService, hostAdapterPath,
45 QDBusConnection::systemBus(), this))
46{
47 // Bluez DBus API doesn't allow distinguishing between advertisement and scan response data;
48 // consolidate the two if they differ.
49 // Union of service UUIDs:
50 if (scanResponseData.services() != advertisingData.services()) {
51 QList<QBluetoothUuid> services = advertisingData.services();
52 for (const auto &service: scanResponseData.services()) {
53 if (!services.contains(service))
54 services.append(service);
55 }
56 m_advData.setServices(services);
57 }
58 // Scan response is given precedence with rest of the data
59 if (!scanResponseData.localName().isEmpty())
60 m_advData.setLocalName(scanResponseData.localName());
62 m_advData.setManufacturerData(scanResponseData.manufacturerId(),
63 scanResponseData.manufacturerData());
64 }
65 if (scanResponseData.includePowerLevel())
66 m_advData.setIncludePowerLevel(true);
67
68 setDataForDBus();
69}
70
75
76// This function parses the advertising data provided by the application and
77// populates the dbus adaptor with it. DBus will ask the data from the adaptor when
78// the advertisement is later registered (started)
79void QLeDBusAdvertiser::setDataForDBus()
80{
81 setAdvertisingParamsForDBus();
82 setAdvertisementDataForDBus();
83}
84
85void QLeDBusAdvertiser::setAdvertisingParamsForDBus()
86{
87 // Whitelist and filter policy
88 if (!m_advParams.whiteList().isEmpty())
89 qCWarning(QT_BT_BLUEZ) << "White lists and filter policies not supported, ignoring";
90
91 // Legacy advertising mode mapped to GAP role (peripheral vs broadcast)
92 switch (m_advParams.mode())
93 {
96 m_advDataDBus->setType(advDataTypeBroadcast);
97 break;
99 default:
100 m_advDataDBus->setType(advDataTypePeripheral);
101 }
102
103 // Advertisement interval (min max in milliseconds). Ensure the values fit the range bluez
104 // allows. The max >= min is guaranteed by QLowEnergyAdvertisingParameters::setInterval().
105 // Note: Bluez reads these values but at the time of this writing it marks this feature
106 // as 'experimental'
108 quint16(m_advParams.minimumInterval()),
111 quint16(m_advParams.maximumInterval()),
113}
114
115void QLeDBusAdvertiser::setAdvertisementDataForDBus()
116{
117 // We don't calculate the advertisement length to guard for too long advertisements.
118 // There isn't adequate control and visibility on the advertisement for that.
119 // - We don't know the max length (legacy or extended advertising)
120 // - Bluez may truncate some of the fields on its own, making calculus here imprecise
121 // - Scan response may or may not be used to offload some of the data
122
123 // Include the power level if requested and dbus supports it
124 const auto supportedIncludes = m_advManager->supportedIncludes();
125 if (m_advData.includePowerLevel() && supportedIncludes.contains(advDataTXPower))
126 m_advDataDBus->setIncludes({advDataTXPower});
127
128 // Set the application provided name (valid to be empty).
129 // For clarity: bluez also has "local-name" system include that could be set if no local
130 // name is provided. However that would require that the LocalName DBus property would
131 // not exist. Existing LocalName property when 'local-name' is included leads to an
132 // advertisement error.
133 m_advDataDBus->setLocalName(m_advData.localName());
134
135 // Service UUIDs
136 if (!m_advData.services().isEmpty()) {
137 QStringList serviceUUIDList;
138 for (const auto& service: m_advData.services())
139 serviceUUIDList << service.toString(QUuid::StringFormat::WithoutBraces);
140 m_advDataDBus->setServiceUUIDs(serviceUUIDList);
141 }
142
143 // Manufacturer data
145 m_advDataDBus->setManufacturerData({
146 {m_advData.manufacturerId(), QDBusVariant(m_advData.manufacturerData())}});
147 }
148
149 // Discoverability
150 if (m_advDataDBus->type() == advDataTypePeripheral) {
151 m_advDataDBus->setDiscoverable(m_advData.discoverability()
153 } else {
154 qCDebug(QT_BT_BLUEZ) << "Ignoring advertisement discoverability in broadcast mode";
155 }
156
157 // Raw data
158 if (!m_advData.rawData().isEmpty())
159 qCWarning(QT_BT_BLUEZ) << "Raw advertisement data not supported, ignoring";
160}
161
163{
164 qCDebug(QT_BT_BLUEZ) << "Start advertising" << m_advObjectPath << "on" << m_advManager->path();
165 if (m_advertising) {
166 qCWarning(QT_BT_BLUEZ) << "Start tried while already advertising";
167 return;
168 }
169
170 if (!QDBusConnection::systemBus().registerObject(m_advObjectPath, m_advDataDBus,
172 qCWarning(QT_BT_BLUEZ) << "Advertisement dbus object registration failed";
174 return;
175 }
176
177 // Register the advertisement which starts the actual advertising.
178 // We use call watcher here instead of waitForFinished() because DBus will
179 // call back our advertisement object (to read data) in this same thread => would block
180 auto reply = m_advManager->RegisterAdvertisement(QDBusObjectPath(m_advObjectPath), {});
182
186 if (reply.isError()) {
187 qCWarning(QT_BT_BLUEZ) << "Advertisement registration failed" << reply.error();
188 if (reply.error().name() == bluezErrorFailed)
189 qCDebug(QT_BT_BLUEZ) << "Advertisement could've been too large";
190 QDBusConnection::systemBus().unregisterObject(m_advObjectPath);
191 emit errorOccurred();
192 } else {
193 qCDebug(QT_BT_BLUEZ) << "Advertisement started successfully";
194 m_advertising = true;
195 }
196 watcher->deleteLater();
197 });
198}
199
201{
202 if (!m_advertising)
203 return;
204
205 m_advertising = false;
206 auto reply = m_advManager->UnregisterAdvertisement(QDBusObjectPath(m_advObjectPath));
207 reply.waitForFinished();
208 if (reply.isError())
209 qCWarning(QT_BT_BLUEZ) << "Error in unregistering advertisement" << reply.error();
210 else
211 qCDebug(QT_BT_BLUEZ) << "Advertisement unregistered successfully";
212 QDBusConnection::systemBus().unregisterObject(m_advObjectPath);
213}
214
215// Called by Bluez when the advertisement has been removed (org.bluez.LEAdvertisement1.Release)
217{
218 qCDebug(QT_BT_BLUEZ) << "Advertisement" << m_advObjectPath << "released"
219 << (m_advertising ? "unexpectedly" : "");
220 if (m_advertising) {
221 // If we are advertising, it means the Release is unsolicited
222 // and handled as an advertisement error. No need to call UnregisterAdvertisement
223 m_advertising = false;
224 QDBusConnection::systemBus().unregisterObject(m_advObjectPath);
226 }
227}
228
QString sanitizeNameForDBus(const QString &text)
std::vector< ObjCStrongReference< CBMutableService > > services
void setType(const QString &value)
void setManufacturerData(ManufacturerDataList value)
void setIncludes(const QStringList &value)
void setServiceUUIDs(const QStringList &value)
void setLocalName(const QString &value)
QDBusPendingReply RegisterAdvertisement(const QDBusObjectPath &advertisement, const QVariantMap &options)
QDBusPendingReply UnregisterAdvertisement(const QDBusObjectPath &advertisement)
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
\inmodule QtCore
QString path() const
Returns the object path that this interface is associated with.
\inmodule QtDBus
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.
\inmodule QtDBus
QLeDBusAdvertiser(const QLowEnergyAdvertisingParameters &params, const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData, const QString &hostAdapterPath, QObject *parent=nullptr)
bool isEmpty() const noexcept
Definition qlist.h:401
void append(parameter_type t)
Definition qlist.h:458
The QLowEnergyAdvertisingData class represents the data to be broadcast during Bluetooth Low Energy a...
QString localName() const
Returns the name of the local device that is to be advertised.
void setIncludePowerLevel(bool doInclude)
Specifies whether to include the device's transmit power level in the advertising data.
void setManufacturerData(quint16 id, const QByteArray &data)
Sets the manufacturer id and data.
QByteArray manufacturerData() const
Returns the manufacturer data.
QByteArray rawData() const
Returns the user-supplied raw data to be advertised.
bool includePowerLevel() const
Returns whether to include the device's transmit power level in the advertising data.
void setLocalName(const QString &name)
Specifies that name should be broadcast as the name of the device.
quint16 manufacturerId() const
Returns the manufacturer id.
Discoverability discoverability() const
Returns the discoverability mode of the advertising device.
QList< QBluetoothUuid > services() const
Returns the list of service UUIDs to be advertised.
void setServices(const QList< QBluetoothUuid > &services)
Specifies that the service UUIDs in services should be advertised.
static quint16 invalidManufacturerId()
Returns an invalid manufacturer id.
The QLowEnergyAdvertisingParameters class represents the parameters used for Bluetooth Low Energy adv...
Mode mode() const
Returns the advertising mode.
int minimumInterval() const
Returns the minimum advertising interval in milliseconds.
int maximumInterval() const
Returns the maximum advertising interval in milliseconds.
QList< AddressInfo > whiteList() const
Returns the white list used for filtering scan and connection requests.
NetworkError error() const
Returns the error that was found during the processing of this request.
\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
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
\inmodule QtCore
Definition quuid.h:31
#define this
Definition dialogs.cpp:9
Combined button and popup list for selecting options.
Q_CORE_EXPORT QtJniTypes::Service service()
static constexpr auto advDataTypePeripheral
static constexpr quint16 advDataMaxIntervalMs
static constexpr auto advObjectPathTemplate
static constexpr quint16 advDataMinIntervalMs
static constexpr auto bluezErrorFailed
static constexpr auto bluezService
static constexpr auto advDataTXPower
static constexpr auto advDataTypeBroadcast
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
void ** params
SSL_CTX int void * arg
#define emit
unsigned short quint16
Definition qtypes.h:48
QFutureWatcher< int > watcher
QNetworkReply * reply
char * toString(const MyType &t)
[31]
QJSValue global