4#include <QtCore/QGlobalStatic>
5#include <QtCore/QLoggingCategory>
7#include <QtCore/QVersionNumber>
8#include <QtNetwork/private/qnet_unix_p.h>
17QT_IMPL_METATYPE_EXTERN(InterfaceList)
18QT_IMPL_METATYPE_EXTERN(ManufacturerDataList)
19QT_IMPL_METATYPE_EXTERN(ServiceDataList)
20QT_IMPL_METATYPE_EXTERN(ManagedObjectList)
23Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
25typedef enum Bluez5TestResultType
33Q_GLOBAL_STATIC_WITH_ARGS(QVersionNumber, bluezDaemonVersion, (QVersionNumber()))
36
37
39void initializeBluez5()
41 if (*bluezVersion() == BluezVersionUnknown) {
42 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral(
"org.bluez"),
44 QDBusConnection::systemBus());
45 qDBusRegisterMetaType<InterfaceList>();
46 qDBusRegisterMetaType<ManagedObjectList>();
47 qDBusRegisterMetaType<ManufacturerDataList>();
48 qDBusRegisterMetaType<ServiceDataList>();
50 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
51 reply.waitForFinished();
52 if (reply.isError()) {
53 *bluezVersion() = BluezNotAvailable;
54 qWarning() <<
"Cannot find a compatible running Bluez. "
55 "Please check the Bluez installation. "
56 "QtBluetooth requires at least BlueZ version 5.";
58 *bluezVersion() = BluezVersion5;
59 qCDebug(QT_BT_BLUEZ) <<
"Bluez 5 detected.";
65
66
67
68
69
70
71
72
76 int hciSocket = ::qt_safe_socket(AF_BLUETOOTH, SOCK_RAW,
BTPROTO_HCI);
78 qCWarning(QT_BT_BLUEZ) <<
"Cannot open HCI socket:" << qt_error_string(errno);
90 if (!devRequestList) {
91 qt_safe_close(hciSocket);
95 QScopedPointer<hci_dev_list_req, QScopedPointerPodDeleter> pDevList(devRequestList);
96 memset(pDevList.data(), 0, devListSize);
98 devRequest = pDevList->dev_req;
100 if (qt_safe_ioctl(hciSocket,
HCIGETDEVLIST, devRequestList) < 0) {
101 qt_safe_close(hciSocket);
102 qCWarning(QT_BT_BLUEZ) <<
"HCI icotl HCIGETDEVLIST:" << qt_error_string(errno);
106 if (devRequestList->dev_num > 0) {
107 devInfo.dev_id = devRequest->dev_id;
109 qt_safe_close(hciSocket);
110 qCWarning(QT_BT_BLUEZ) <<
"HCI icotl HCIGETDEVINFO:" << qt_error_string(errno);
116 const int maxNoOfConnections = 20;
122 qt_safe_close(hciSocket);
126 QScopedPointer<hci_conn_list_req, QScopedPointerPodDeleter> pInfoList(infoList);
127 pInfoList->conn_num = maxNoOfConnections;
128 pInfoList->dev_id = devInfo.dev_id;
130 if (qt_safe_ioctl(hciSocket,
HCIGETCONNLIST, (
void *) infoList) < 0) {
131 qCWarning(QT_BT_BLUEZ) <<
"HCI icotl HCIGETCONNLIST:" << qt_error_string(errno);
132 qt_safe_close(hciSocket);
136 qt_safe_close(hciSocket);
141
142
143
144
145
146
147
148
149
150
151
152
153
154
157 if (bluezDaemonVersion()->isNull()) {
166 qCDebug(QT_BT_BLUEZ) <<
"Detecting bluetoothd version";
170 const QString version = qEnvironmentVariable(
"BLUETOOTH_FORCE_DBUS_LE_VERSION");
171 if (!version.isNull()) {
172 const QVersionNumber vn = QVersionNumber::fromString(version);
174 *bluezDaemonVersion() = vn;
175 qCDebug(QT_BT_BLUEZ) <<
"Forcing Bluez LE API selection:"
176 << bluezDaemonVersion()->toString();
181 if (bluezDaemonVersion()->isNull() && qt_haveLinuxProcfs()) {
182 QDBusConnection session = QDBusConnection::systemBus();
183 qint64 pid = session.interface()->servicePid(QStringLiteral(
"org.bluez")).value();
186 auto determineBinaryVersion = [](
const QString &binary) -> QVersionNumber {
188 process.start(binary, {QStringLiteral(
"--version")});
189 process.waitForFinished();
191 const QString version = QString::fromLocal8Bit(process.readAll());
192 const QVersionNumber vn = QVersionNumber::fromString(version);
194 qCDebug(QT_BT_BLUEZ) <<
"Detected bluetoothd version" << vn;
199 qCDebug(QT_BT_BLUEZ) <<
"Using /proc/<pid>/exe";
200 const QString procExe = QStringLiteral(
"/proc/%1/exe").arg(pid);
201 const QVersionNumber vn = determineBinaryVersion(procExe);
203 *bluezDaemonVersion() = vn;
205 if (bluezDaemonVersion()->isNull()) {
206 qCDebug(QT_BT_BLUEZ) <<
"Using /proc/<pid>/cmdline";
208 QFile procFile(QStringLiteral(
"/proc/%1/cmdline").arg(pid));
209 if (procFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
210 buffer = procFile.readAll();
214 const QString binary = QString::fromLocal8Bit(buffer.split(
'\0').at(0));
215 QFileInfo info(binary);
216 if (info.isExecutable())
217 *bluezDaemonVersion() = determineBinaryVersion(binary);
219 qCDebug(QT_BT_BLUEZ) <<
"Cannot determine bluetoothd version via cmdline:"
226 if (bluezDaemonVersion()->isNull()) {
230 *bluezDaemonVersion() = QVersionNumber(4, 0);
235 if (bluezDaemonVersion()->isNull()) {
238 *bluezDaemonVersion() = QVersionNumber(3, 0);
239 qCWarning(QT_BT_BLUEZ) <<
"Cannot determine bluetoothd version and required Bluetooth HCI ioctols";
240 qCWarning(QT_BT_BLUEZ) <<
"Disabling Qt Bluetooth LE feature";
243 qCDebug(QT_BT_BLUEZ) <<
"Bluetoothd:" << bluezDaemonVersion()->toString();
246 Q_ASSERT(!bluezDaemonVersion()->isNull());
247 return *bluezDaemonVersion();
264 OrgFreedesktopDBusObjectManagerInterface *
manager =
nullptr;
270
271
272
273
274
275
276
277
278
279
280
281
282
283
288 qCDebug(QT_BT_BLUEZ) <<
"Creating QtBluezDiscoveryManager";
289 d =
new QtBluezDiscoveryManagerPrivate();
291 d->manager =
new OrgFreedesktopDBusObjectManagerInterface(
292 QStringLiteral(
"org.bluez"), QStringLiteral(
"/"),
293 QDBusConnection::systemBus(),
this);
294 connect(d->manager, SIGNAL(InterfacesRemoved(QDBusObjectPath,QStringList)),
295 SLOT(InterfacesRemoved(QDBusObjectPath,QStringList)));
298QtBluezDiscoveryManager::~QtBluezDiscoveryManager()
300 qCDebug(QT_BT_BLUEZ) <<
"Destroying QtBluezDiscoveryManager";
302 const QList<QString> adapterPaths = d->references.keys();
303 for (
const QString &adapterPath : adapterPaths) {
304 AdapterData *data = d->references.take(adapterPath);
305 delete data->propteryListener;
308 if (!data->wasListeningAlready) {
309 OrgBluezAdapter1Interface iface(QStringLiteral(
"org.bluez"), adapterPath,
310 QDBusConnection::systemBus());
311 iface.StopDiscovery();
322 return discoveryManager();
327 if (adapterPath.isEmpty())
331 if (d->references.contains(adapterPath)) {
332 d->references[adapterPath]->reference++;
339 QStringLiteral(
"org.bluez"), adapterPath, QDBusConnection::systemBus());
340 connect(propIface, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
341 this, &QtBluezDiscoveryManager::PropertiesChanged);
345 QDBusConnection::systemBus());
348 d->references[adapterPath] = data;
351 iface.StartDiscovery();
358 if (!d->references.contains(adapterPath))
367 d->references.remove(adapterPath);
370 QDBusConnection::systemBus());
371 iface.StopDiscovery();
393 const QStringList &interfaces)
395 if (!d->references.contains(object_path.path())
396 || !interfaces.contains(QStringLiteral(
"org.bluez.Adapter1")))
399 removeAdapterFromMonitoring(object_path.path());
403 const QVariantMap &changed_properties,
404 const QStringList &invalidated_properties,
405 const QDBusMessage &)
407 Q_UNUSED(invalidated_properties);
415 if (interface == QStringLiteral(
"org.bluez.Adapter1")
416 && d->references.contains(propIface->path())
417 && changed_properties.contains(QStringLiteral(
"Discovering"))) {
418 bool isDiscovering = changed_properties.value(QStringLiteral(
"Discovering")).toBool();
419 if (!isDiscovering) {
422
423
424
425
426
428 AdapterData *data = d->references[propIface->path()];
430 removeAdapterFromMonitoring(propIface->path());
433 QDBusConnection::systemBus());
434 iface.StartDiscovery();
447 emit discoveryInterrupted(dbusPath);
451
452
453
454
455
456
457
458
461 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral(
"org.bluez"),
463 QDBusConnection::systemBus());
465 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
466 reply.waitForFinished();
467 if (reply.isError()) {
474 typedef QPair<QString, QBluetoothAddress> AddressForPathType;
475 QList<AddressForPathType> localAdapters;
478 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
479 const QDBusObjectPath &path = it.key();
482 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
483 const QString &iface = jt.key();
485 if (iface == QStringLiteral(
"org.bluez.Adapter1")) {
486 AddressForPathType pair;
487 pair.first = path.path();
488 pair.second = QBluetoothAddress(ifaceList.value(iface).value(
489 QStringLiteral(
"Address")).toString());
490 if (!pair.second.isNull())
491 localAdapters.append(pair);
500 if (localAdapters.isEmpty())
503 if (wantedAddress.isNull())
504 return localAdapters.front().first;
506 for (
const AddressForPathType &pair : std::as_const(localAdapters)) {
507 if (pair.second == wantedAddress)
515
516
517
518
526 const QString hostAdapterPath = findAdapterForAddress(localAddress, &ok);
527 if (!ok || hostAdapterPath.isEmpty())
531 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral(
"org.bluez"),
533 QDBusConnection::systemBus());
534 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
535 reply.waitForFinished();
539 using namespace Qt::StringLiterals;
541 const bool peripheralSupported = reply.value()
542 .value(QDBusObjectPath(hostAdapterPath))
543 .contains(
"org.bluez.LEAdvertisingManager1"_L1);
545 qCDebug(QT_BT_BLUEZ) <<
"Peripheral role"
546 << (peripheralSupported ?
"" :
"not")
547 <<
"supported on" << hostAdapterPath;
548 return peripheralSupported ? hostAdapterPath : QString{};
552
553
554
555
558 QString appName = text;
559 for (qsizetype i = 0; i < appName.size(); ++i) {
560 ushort us = appName[i].unicode();
561 bool valid = (us >=
'a' && us <=
'z')
562 || (us >=
'A' && us <=
'Z')
563 || (us >=
'0' && us <=
'9')
567 appName[i] = QLatin1Char(
'_');
575#include "moc_bluez5_helper_p.cpp"
QString adapterWithDBusPeripheralInterface(const QBluetoothAddress &localAddress)
QString sanitizeNameForDBus(const QString &text)
QString findAdapterForAddress(const QBluetoothAddress &wantedAddress, bool *ok=nullptr)
bool mandatoryHciIoctlsAvailable()
QVersionNumber bluetoothdVersion()
QMap< QDBusObjectPath, InterfaceList > ManagedObjectList
QMap< QString, QVariantMap > InterfaceList
QMap< QString, QDBusVariant > ServiceDataList
QMap< quint16, QDBusVariant > ManufacturerDataList
QMap< QString, AdapterData * > references
OrgFreedesktopDBusObjectManagerInterface * manager
static QtBluezDiscoveryManager * instance()
bool registerDiscoveryInterest(const QString &adapterPath)
void unregisterDiscoveryInterest(const QString &adapterPath)
OrgFreedesktopDBusPropertiesInterface * propteryListener