4#include <QtCore/QLoggingCategory>
6#include <QtCore/qcoreapplication.h>
13#include "bluez/bluez5_helper_p.h"
14#include "bluez/objectmanager_p.h"
15#include "bluez/adapter1_bluez5_p.h"
16#include "bluez/device1_bluez5_p.h"
17#include "bluez/properties_p.h"
18#include "bluez/bluetoothmanagement_p.h"
22Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
24using namespace QtBluetoothPrivate;
26QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
27 const QBluetoothAddress &deviceAdapter, QBluetoothDeviceDiscoveryAgent *parent) :
28 adapterAddress(deviceAdapter),
32 manager =
new OrgFreedesktopDBusObjectManagerInterface(
33 QStringLiteral(
"org.bluez"),
35 QDBusConnection::systemBus(), parent);
36 QObject::connect(manager,
37 &OrgFreedesktopDBusObjectManagerInterface::InterfacesAdded,
39 [
this](
const QDBusObjectPath &objectPath, InterfaceList interfacesAndProperties) {
40 this->_q_InterfacesAdded(objectPath, interfacesAndProperties);
44 BluetoothManagement::instance();
47QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
59bool QBluetoothDeviceDiscoveryAgentPrivate::isActive()
const
69QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
71 return (ClassicMethod | LowEnergyMethod);
74void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
76 if (pendingCancel ==
true) {
81 lastError = QBluetoothDeviceDiscoveryAgent::NoError;
83 discoveredDevices.clear();
84 devicesProperties.clear();
86 Q_Q(QBluetoothDeviceDiscoveryAgent);
89 const QString adapterPath = findAdapterForAddress(adapterAddress, &ok);
90 if (!ok || adapterPath.isEmpty()) {
91 qCWarning(QT_BT_BLUEZ) <<
"Cannot find Bluez 5 adapter for device search" << ok;
92 lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
93 errorString = QBluetoothDeviceDiscoveryAgent::tr(
"Cannot find valid Bluetooth adapter.");
94 q->errorOccurred(lastError);
98 adapter =
new OrgBluezAdapter1Interface(QStringLiteral(
"org.bluez"), adapterPath,
99 QDBusConnection::systemBus());
101 if (!adapter->powered()) {
102 qCDebug(QT_BT_BLUEZ) <<
"Aborting device discovery due to offline Bluetooth Adapter";
103 lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError;
104 errorString = QBluetoothDeviceDiscoveryAgent::tr(
"Device is powered off");
107 emit q->errorOccurred(lastError);
112 if (methods == (QBluetoothDeviceDiscoveryAgent::LowEnergyMethod|QBluetoothDeviceDiscoveryAgent::ClassicMethod))
113 map.insert(QStringLiteral(
"Transport"), QStringLiteral(
"auto"));
114 else if (methods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)
115 map.insert(QStringLiteral(
"Transport"), QStringLiteral(
"le"));
117 map.insert(QStringLiteral(
"Transport"), QStringLiteral(
"bredr"));
121 QDBusPendingReply<> filterReply = adapter->SetDiscoveryFilter(map);
122 filterReply.waitForFinished();
123 if (filterReply.isError()) {
124 if (filterReply.error().type() == QDBusError::Other
125 && filterReply.error().name() == QStringLiteral(
"org.bluez.Error.Failed")) {
126 qCDebug(QT_BT_BLUEZ) <<
"Discovery method" << methods <<
"not supported";
127 lastError = QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod;
128 errorString = QBluetoothDeviceDiscoveryAgent::tr(
"One or more device discovery methods "
129 "are not supported on this platform");
132 emit q->errorOccurred(lastError);
134 }
else if (filterReply.error().type() != QDBusError::UnknownMethod) {
135 qCDebug(QT_BT_BLUEZ) <<
"SetDiscoveryFilter failed:" << filterReply.error();
139 QtBluezDiscoveryManager::instance()->registerDiscoveryInterest(adapter->path());
140 QObject::connect(QtBluezDiscoveryManager::instance(), &QtBluezDiscoveryManager::discoveryInterrupted,
141 q, [
this](
const QString &path){
142 this->_q_discoveryInterrupted(path);
144 OrgFreedesktopDBusPropertiesInterface *prop =
new OrgFreedesktopDBusPropertiesInterface(
145 QStringLiteral(
"org.bluez"), QStringLiteral(
""), QDBusConnection::systemBus());
146 QObject::connect(prop, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
147 q, [
this](
const QString &interface,
const QVariantMap &changedProperties,
148 const QStringList &invalidatedProperties,
149 const QDBusMessage &signal) {
150 this->_q_PropertiesChanged(interface, signal.path(), changedProperties, invalidatedProperties);
154 propertyMonitors.append(prop);
157 QDBusPendingReply<ManagedObjectList> reply = manager->GetManagedObjects();
158 reply.waitForFinished();
159 if (!reply.isError()) {
160 ManagedObjectList managedObjectList = reply.value();
161 for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
162 const QDBusObjectPath &path = it.key();
163 const InterfaceList &ifaceList = it.value();
165 for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
166 const QString &iface = jt.key();
168 if (iface == QStringLiteral(
"org.bluez.Device1")) {
170 if (path.path().indexOf(adapter->path()) != 0)
173 deviceFound(path.path(), jt.value());
182 if (!discoveryTimer) {
183 discoveryTimer =
new QTimer(q);
184 discoveryTimer->setSingleShot(
true);
185 QObject::connect(discoveryTimer, &QTimer::timeout,
187 this->_q_discoveryFinished();
191 if (lowEnergySearchTimeout > 0) {
192 discoveryTimer->setInterval(lowEnergySearchTimeout);
193 discoveryTimer->start();
197void QBluetoothDeviceDiscoveryAgentPrivate::stop()
202 qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO;
203 pendingCancel =
true;
204 pendingStart =
false;
205 _q_discoveryFinished();
211 const QBluetoothAddress btAddress(properties[QStringLiteral(
"Address")].toString());
212 if (btAddress.isNull())
213 return QBluetoothDeviceInfo();
215 const QString btName = properties[QStringLiteral(
"Alias")].toString();
216 quint32 btClass = properties[QStringLiteral(
"Class")].toUInt();
218 QBluetoothDeviceInfo deviceInfo(btAddress, btName, btClass);
219 deviceInfo.setRssi(qvariant_cast<
short>(properties[QStringLiteral(
"RSSI")]));
221 QList<QBluetoothUuid> uuids;
222 bool foundLikelyLowEnergyUuid =
false;
223 const QStringList foundUuids = qvariant_cast<QStringList>(properties[QStringLiteral(
"UUIDs")]);
224 for (
const auto &u: foundUuids) {
225 const QBluetoothUuid id(u);
229 if (!foundLikelyLowEnergyUuid) {
232 quint16 shortId = id.toUInt16(&ok);
233 quint16 genericAccessInt =
static_cast<quint16>(QBluetoothUuid::ServiceClassUuid::GenericAccess);
234 if (ok && ((shortId & genericAccessInt) == genericAccessInt))
235 foundLikelyLowEnergyUuid =
true;
239 deviceInfo.setServiceUuids(uuids);
242 deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
244 deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
245 if (foundLikelyLowEnergyUuid)
246 deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
250 const QList<quint16> keysManufacturer = deviceManufacturerData.keys();
251 for (quint16 key : keysManufacturer)
252 deviceInfo.setManufacturerData(
253 key, deviceManufacturerData.value(key).variant().toByteArray());
256 qdbus_cast<
ServiceDataList>(properties[QStringLiteral(
"ServiceData")]);
257 const QList<QString> keysService = deviceServiceData.keys();
258 for (QString key : keysService)
259 deviceInfo.setServiceData(QBluetoothUuid(key),
260 deviceServiceData.value(key).variant().toByteArray());
265void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(
const QString &devicePath,
266 const QVariantMap &properties)
268 Q_Q(QBluetoothDeviceDiscoveryAgent);
273 auto deviceAdapter = qvariant_cast<QDBusObjectPath>(properties[QStringLiteral(
"Adapter")]);
274 if (deviceAdapter.path() != adapter->path())
278 QBluetoothDeviceInfo deviceInfo = createDeviceInfoFromBluez5Device(properties);
279 if (!deviceInfo.isValid())
282 qCDebug(QT_BT_BLUEZ) <<
"Discovered: " << deviceInfo.name() << deviceInfo.address()
283 <<
"Num UUIDs" << deviceInfo.serviceUuids().size()
284 <<
"total device" << discoveredDevices.size() <<
"cached"
285 <<
"RSSI" << deviceInfo.rssi()
286 <<
"Num ManufacturerData" << deviceInfo.manufacturerData().size()
287 <<
"Num ServiceData" << deviceInfo.serviceData().size();
290 devicesProperties[devicePath] = properties;
292 for (qsizetype i = 0; i < discoveredDevices.size(); ++i) {
293 if (discoveredDevices[i].address() == deviceInfo.address()) {
294 if (lowEnergySearchTimeout > 0 && discoveredDevices[i] == deviceInfo) {
295 qCDebug(QT_BT_BLUEZ) <<
"Duplicate: " << deviceInfo.address();
298 discoveredDevices.replace(i, deviceInfo);
300 emit q->deviceDiscovered(deviceInfo);
305 discoveredDevices.append(deviceInfo);
306 emit q->deviceDiscovered(deviceInfo);
309void QBluetoothDeviceDiscoveryAgentPrivate::_q_InterfacesAdded(
const QDBusObjectPath &object_path,
310 InterfaceList interfaces_and_properties)
312 Q_Q(QBluetoothDeviceDiscoveryAgent);
317 if (interfaces_and_properties.contains(QStringLiteral(
"org.bluez.Device1"))) {
320 deviceFound(object_path.path(),
321 interfaces_and_properties[QStringLiteral(
"org.bluez.Device1")]);
325void QBluetoothDeviceDiscoveryAgentPrivate::_q_discoveryFinished()
327 Q_Q(QBluetoothDeviceDiscoveryAgent);
330 discoveryTimer->stop();
332 QtBluezDiscoveryManager::instance()->disconnect(q);
333 QtBluezDiscoveryManager::instance()->unregisterDiscoveryInterest(adapter->path());
335 qDeleteAll(propertyMonitors);
336 propertyMonitors.clear();
341 if (pendingCancel && !pendingStart) {
342 pendingCancel =
false;
344 }
else if (pendingStart) {
345 pendingStart =
false;
346 pendingCancel =
false;
347 start(QBluetoothDeviceDiscoveryAgent::ClassicMethod
348 | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
354void QBluetoothDeviceDiscoveryAgentPrivate::_q_discoveryInterrupted(
const QString &path)
356 Q_Q(QBluetoothDeviceDiscoveryAgent);
361 if (path == adapter->path()) {
362 qCWarning(QT_BT_BLUEZ) <<
"Device discovery aborted due to unexpected adapter changes from another process.";
365 discoveryTimer->stop();
367 QtBluezDiscoveryManager::instance()->disconnect(q);
374 errorString = QBluetoothDeviceDiscoveryAgent::tr(
"Bluetooth adapter error");
375 lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
376 emit q->errorOccurred(lastError);
380void QBluetoothDeviceDiscoveryAgentPrivate::_q_PropertiesChanged(
const QString &interface,
382 const QVariantMap &changed_properties,
383 const QStringList &invalidated_properties)
385 Q_Q(QBluetoothDeviceDiscoveryAgent);
386 if (interface != QStringLiteral(
"org.bluez.Device1"))
389 if (!devicesProperties.contains(path))
394 QVariantMap & properties = devicesProperties[path];
395 for (QVariantMap::const_iterator it = changed_properties.constBegin();
396 it != changed_properties.constEnd(); ++it) {
397 properties[it.key()] = it.value();
400 for (
const QString & property : invalidated_properties)
401 properties.remove(property);
403 const auto info = createDeviceInfoFromBluez5Device(properties);
407 if (changed_properties.contains(QStringLiteral(
"RSSI"))
408 || changed_properties.contains(QStringLiteral(
"ManufacturerData"))) {
410 for (qsizetype i = 0; i < discoveredDevices.size(); ++i) {
411 if (discoveredDevices[i].address() == info.address()) {
412 QBluetoothDeviceInfo::Fields updatedFields = QBluetoothDeviceInfo::Field::None;
413 if (changed_properties.contains(QStringLiteral(
"RSSI"))) {
414 qCDebug(QT_BT_BLUEZ) <<
"Updating RSSI for" << info.address()
415 << changed_properties.value(QStringLiteral(
"RSSI"));
416 discoveredDevices[i].setRssi(
417 changed_properties.value(QStringLiteral(
"RSSI")).toInt());
418 updatedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI);
420 if (changed_properties.contains(QStringLiteral(
"ManufacturerData"))) {
421 qCDebug(QT_BT_BLUEZ) <<
"Updating ManufacturerData for" << info.address();
422 ManufacturerDataList changedManufacturerData =
423 qdbus_cast< ManufacturerDataList >(changed_properties.value(QStringLiteral(
"ManufacturerData")));
425 const QList<quint16> keys = changedManufacturerData.keys();
426 bool wasNewValue =
false;
427 for (quint16 key : keys) {
428 bool added = discoveredDevices[i].setManufacturerData(key, changedManufacturerData.value(key).variant().toByteArray());
429 wasNewValue = (wasNewValue || added);
433 updatedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
436 if (lowEnergySearchTimeout > 0) {
437 if (discoveredDevices[i] != info) {
438 if (discoveredDevices.at(i).name() == info.name()) {
439 qCDebug(QT_BT_BLUEZ) <<
"Almost Duplicate " << info.address()
440 << info.name() <<
"- replacing in place";
441 discoveredDevices.replace(i, info);
442 emit q->deviceDiscovered(info);
445 if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None))
446 emit q->deviceUpdated(discoveredDevices[i], updatedFields);
452 discoveredDevices.replace(i, info);
453 emit q_ptr->deviceDiscovered(discoveredDevices[i]);
455 if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None))
456 emit q->deviceUpdated(discoveredDevices[i], updatedFields);
QMap< QString, QDBusVariant > ServiceDataList
QMap< quint16, QDBusVariant > ManufacturerDataList
static QBluetoothDeviceInfo createDeviceInfoFromBluez5Device(const QVariantMap &properties)