36Q_GLOBAL_STATIC_WITH_ARGS(QVersionNumber, bluezDaemonVersion, (QVersionNumber()))
39
40
42void initializeBluez5()
44 if (*bluezVersion() == BluezVersionUnknown) {
45 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral(
"org.bluez"),
47 QDBusConnection::systemBus());
48 qDBusRegisterMetaType<InterfaceList>();
49 qDBusRegisterMetaType<ManagedObjectList>();
50 qDBusRegisterMetaType<ManufacturerDataList>();
51 qDBusRegisterMetaType<ServiceDataList>();
53 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
54 reply.waitForFinished();
55 if (reply.isError()) {
56 *bluezVersion() = BluezNotAvailable;
57 qWarning() <<
"Cannot find a compatible running Bluez. "
58 "Please check the Bluez installation. "
59 "QtBluetooth requires at least BlueZ version 5.";
61 *bluezVersion() = BluezVersion5;
62 qCDebug(QT_BT_BLUEZ) <<
"Bluez 5 detected.";
76bool mandatoryHciIoctlsAvailable()
79 int hciSocket = ::qt_safe_socket(AF_BLUETOOTH, SOCK_RAW,
BTPROTO_HCI);
81 qCWarning(QT_BT_BLUEZ) <<
"Cannot open HCI socket:" << qt_error_string(errno);
86 struct hci_dev_req *devRequest =
nullptr;
87 struct hci_dev_list_req *devRequestList =
nullptr;
88 struct hci_dev_info devInfo;
89 const int devListSize =
sizeof(
struct hci_dev_list_req)
92 devRequestList = (hci_dev_list_req *) malloc(devListSize);
93 if (!devRequestList) {
94 qt_safe_close(hciSocket);
98 QScopedPointer<hci_dev_list_req, QScopedPointerPodDeleter> pDevList(devRequestList);
99 memset(pDevList.data(), 0, devListSize);
101 devRequest = pDevList->dev_req;
103 if (qt_safe_ioctl(hciSocket,
HCIGETDEVLIST, devRequestList) < 0) {
104 qt_safe_close(hciSocket);
105 qCWarning(QT_BT_BLUEZ) <<
"HCI icotl HCIGETDEVLIST:" << qt_error_string(errno);
109 if (devRequestList->dev_num > 0) {
110 devInfo.dev_id = devRequest->dev_id;
112 qt_safe_close(hciSocket);
113 qCWarning(QT_BT_BLUEZ) <<
"HCI icotl HCIGETDEVINFO:" << qt_error_string(errno);
119 const int maxNoOfConnections = 20;
120 hci_conn_list_req *infoList =
nullptr;
121 infoList = (hci_conn_list_req *)
122 malloc(
sizeof(hci_conn_list_req) + maxNoOfConnections *
sizeof(hci_conn_info));
125 qt_safe_close(hciSocket);
129 QScopedPointer<hci_conn_list_req, QScopedPointerPodDeleter> pInfoList(infoList);
130 pInfoList->conn_num = maxNoOfConnections;
131 pInfoList->dev_id = devInfo.dev_id;
133 if (qt_safe_ioctl(hciSocket,
HCIGETCONNLIST, (
void *) infoList) < 0) {
134 qCWarning(QT_BT_BLUEZ) <<
"HCI icotl HCIGETCONNLIST:" << qt_error_string(errno);
135 qt_safe_close(hciSocket);
139 qt_safe_close(hciSocket);
158QVersionNumber bluetoothdVersion()
160 if (bluezDaemonVersion()->isNull()) {
164 qDBusRegisterMetaType<InterfaceList>();
165 qDBusRegisterMetaType<ManagedObjectList>();
166 qDBusRegisterMetaType<ManufacturerDataList>();
167 qDBusRegisterMetaType<ServiceDataList>();
169 qCDebug(QT_BT_BLUEZ) <<
"Detecting bluetoothd version";
173 const QString version = qEnvironmentVariable(
"BLUETOOTH_FORCE_DBUS_LE_VERSION");
174 if (!version.isNull()) {
175 const QVersionNumber vn = QVersionNumber::fromString(version);
177 *bluezDaemonVersion() = vn;
178 qCDebug(QT_BT_BLUEZ) <<
"Forcing Bluez LE API selection:"
179 << bluezDaemonVersion()->toString();
184 if (bluezDaemonVersion()->isNull() && qt_haveLinuxProcfs()) {
185 QDBusConnection session = QDBusConnection::systemBus();
186 qint64 pid = session.interface()->servicePid(QStringLiteral(
"org.bluez")).value();
189 auto determineBinaryVersion = [](
const QString &binary) -> QVersionNumber {
191 process.start(binary, {QStringLiteral(
"--version")});
192 process.waitForFinished();
194 const QString version = QString::fromLocal8Bit(process.readAll());
195 const QVersionNumber vn = QVersionNumber::fromString(version);
197 qCDebug(QT_BT_BLUEZ) <<
"Detected bluetoothd version" << vn;
202 qCDebug(QT_BT_BLUEZ) <<
"Using /proc/<pid>/exe";
203 const QString procExe = QStringLiteral(
"/proc/%1/exe").arg(pid);
204 const QVersionNumber vn = determineBinaryVersion(procExe);
206 *bluezDaemonVersion() = vn;
208 if (bluezDaemonVersion()->isNull()) {
209 qCDebug(QT_BT_BLUEZ) <<
"Using /proc/<pid>/cmdline";
211 QFile procFile(QStringLiteral(
"/proc/%1/cmdline").arg(pid));
212 if (procFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
213 buffer = procFile.readAll();
217 const QString binary = QString::fromLocal8Bit(buffer.split(
'\0').at(0));
218 QFileInfo info(binary);
219 if (info.isExecutable())
220 *bluezDaemonVersion() = determineBinaryVersion(binary);
222 qCDebug(QT_BT_BLUEZ) <<
"Cannot determine bluetoothd version via cmdline:"
229 if (bluezDaemonVersion()->isNull()) {
231 if (mandatoryHciIoctlsAvailable()) {
233 *bluezDaemonVersion() = QVersionNumber(4, 0);
238 if (bluezDaemonVersion()->isNull()) {
241 *bluezDaemonVersion() = QVersionNumber(3, 0);
242 qCWarning(QT_BT_BLUEZ) <<
"Cannot determine bluetoothd version and required Bluetooth HCI ioctols";
243 qCWarning(QT_BT_BLUEZ) <<
"Disabling Qt Bluetooth LE feature";
246 qCDebug(QT_BT_BLUEZ) <<
"Bluetoothd:" << bluezDaemonVersion()->toString();
249 Q_ASSERT(!bluezDaemonVersion()->isNull());
250 return *bluezDaemonVersion();
291 qCDebug(QT_BT_BLUEZ) <<
"Creating QtBluezDiscoveryManager";
292 d =
new QtBluezDiscoveryManagerPrivate();
294 d->manager =
new OrgFreedesktopDBusObjectManagerInterface(
295 QStringLiteral(
"org.bluez"), QStringLiteral(
"/"),
296 QDBusConnection::systemBus(),
this);
297 connect(d->manager, SIGNAL(InterfacesRemoved(QDBusObjectPath,QStringList)),
298 SLOT(InterfacesRemoved(QDBusObjectPath,QStringList)));
301QtBluezDiscoveryManager::~QtBluezDiscoveryManager()
303 qCDebug(QT_BT_BLUEZ) <<
"Destroying QtBluezDiscoveryManager";
305 const QList<QString> adapterPaths = d->references.keys();
306 for (
const QString &adapterPath : adapterPaths) {
307 AdapterData *data = d->references.take(adapterPath);
308 delete data->propteryListener;
311 if (!data->wasListeningAlready) {
312 OrgBluezAdapter1Interface iface(QStringLiteral(
"org.bluez"), adapterPath,
313 QDBusConnection::systemBus());
314 iface.StopDiscovery();
330 if (adapterPath.isEmpty())
334 if (d->references.contains(adapterPath)) {
335 d->references[adapterPath]->reference++;
339 AdapterData *data =
new AdapterData();
341 OrgFreedesktopDBusPropertiesInterface *propIface =
new OrgFreedesktopDBusPropertiesInterface(
342 QStringLiteral(
"org.bluez"), adapterPath, QDBusConnection::systemBus());
343 connect(propIface, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
344 this, &QtBluezDiscoveryManager::PropertiesChanged);
345 data->propteryListener = propIface;
347 OrgBluezAdapter1Interface iface(QStringLiteral(
"org.bluez"), adapterPath,
348 QDBusConnection::systemBus());
349 data->wasListeningAlready = iface.discovering();
351 d->references[adapterPath] = data;
353 if (!data->wasListeningAlready)
354 iface.StartDiscovery();
361 if (!d->references.contains(adapterPath))
364 AdapterData *data = d->references[adapterPath];
367 if (data->reference > 0)
370 d->references.remove(adapterPath);
371 if (!data->wasListeningAlready) {
372 OrgBluezAdapter1Interface iface(QStringLiteral(
"org.bluez"), adapterPath,
373 QDBusConnection::systemBus());
374 iface.StopDiscovery();
377 delete data->propteryListener;
466 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral(
"org.bluez"),
468 QDBusConnection::systemBus());
470 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
471 reply.waitForFinished();
472 if (reply.isError()) {
482 QBluetoothAddress address;
485 QList<AdapterInfo> localAdapters;
488 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
489 const QDBusObjectPath &path = it.key();
492 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
493 const QString &iface = jt.key();
495 if (iface == QStringLiteral(
"org.bluez.Adapter1")) {
497 info.path = path.path();
498 info.address = QBluetoothAddress(ifaceList.value(iface).value(
499 QStringLiteral(
"Address")).toString());
500 info.powered = ifaceList.value(iface).value(QStringLiteral(
"Powered")).toBool();
501 if (!info.address.isNull())
502 localAdapters.append(info);
511 if (localAdapters.isEmpty())
514 auto findFirstPowered = [&localAdapters]() {
515 Q_ASSERT(!localAdapters.isEmpty());
516 auto it = std::find_if(localAdapters.cbegin(), localAdapters.cend(),
517 [](
const AdapterInfo &info) {
518 return info.powered ==
true;
520 if (it != localAdapters.cend())
523 return localAdapters.cbegin()->path;
526 if (wantedAddress.isNull())
527 return findFirstPowered();
529 for (
const AdapterInfo &info : std::as_const(localAdapters)) {
530 if (info.address == wantedAddress)
549 const QString hostAdapterPath = findAdapterForAddress(localAddress, &ok);
550 if (!ok || hostAdapterPath.isEmpty())
554 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral(
"org.bluez"),
556 QDBusConnection::systemBus());
557 QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
558 reply.waitForFinished();
562 using namespace Qt::StringLiterals;
564 const bool peripheralSupported = reply.value()
565 .value(QDBusObjectPath(hostAdapterPath))
566 .contains(
"org.bluez.LEAdvertisingManager1"_L1);
568 qCDebug(QT_BT_BLUEZ) <<
"Peripheral role"
569 << (peripheralSupported ?
"" :
"not")
570 <<
"supported on" << hostAdapterPath;
571 return peripheralSupported ? hostAdapterPath : QString{};