summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@qt.io>2017-11-29 15:15:51 +0100
committerAlex Blasche <alexander.blasche@qt.io>2018-01-05 09:06:48 +0000
commitda7a013dd2aa7bed9f4a6b5122b8f9dadcb252ab (patch)
tree6fa82dce354eec8871ab048caafddd85daa1cb49 /src
parent37a3b8104588605f9055ea93e59112e48a114dc1 (diff)
BTLE DBus: Add ability to connect/disconnect to remote device
The patch uses the Device1 DBus API to connect and disconnect to a remote BTLE device. In addition, status and error condition tracking is added to adapt to various error cases. Task-number: QTBUG-46819 Change-Id: I0671df5596882c89aeead89c05ddcc855f8ba375 Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/bluetooth/qlowenergycontroller_bluezdbus.cpp244
-rw-r--r--src/bluetooth/qlowenergycontroller_bluezdbus_p.h28
2 files changed, 268 insertions, 4 deletions
diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
index 95721e0c..1829f153 100644
--- a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp
@@ -38,10 +38,17 @@
****************************************************************************/
#include "qlowenergycontroller_bluezdbus_p.h"
+#include "bluez/adapter1_bluez5_p.h"
+#include "bluez/bluez5_helper_p.h"
+#include "bluez/device1_bluez5_p.h"
+#include "bluez/objectmanager_p.h"
+#include "bluez/properties_p.h"
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
+
QLowEnergyControllerPrivateBluezDBus::QLowEnergyControllerPrivateBluezDBus()
: QLowEnergyControllerPrivate()
{
@@ -55,15 +62,248 @@ void QLowEnergyControllerPrivateBluezDBus::init()
{
}
+void QLowEnergyControllerPrivateBluezDBus::devicePropertiesChanged(
+ const QString &interface, const QVariantMap &changedProperties,
+ const QStringList &/*removedProperties*/)
+{
+ if (interface == QStringLiteral("org.bluez.Device1")) {
+ qCDebug(QT_BT_BLUEZ) << "######" << interface << changedProperties;
+ if (changedProperties.contains(QStringLiteral("ServicesResolved"))) {
+ //we could check for Connected property as well, but we choose to wait
+ //for ServicesResolved being true
+
+ if (pendingConnect) {
+ bool isResolved = changedProperties.value(QStringLiteral("ServicesResolved")).toBool();
+ if (isResolved) {
+ setState(QLowEnergyController::ConnectedState);
+ pendingConnect = false;
+ disconnectSignalRequired = true;
+ Q_Q(QLowEnergyController);
+ emit q->connected();
+ }
+ }
+ }
+
+ if (changedProperties.contains(QStringLiteral("Connected"))) {
+ bool isConnected = changedProperties.value(QStringLiteral("Connected")).toBool();
+ if (!isConnected) {
+ switch (state) {
+ case QLowEnergyController::ConnectingState:
+ case QLowEnergyController::ConnectedState:
+ case QLowEnergyController::DiscoveringState:
+ case QLowEnergyController::DiscoveredState:
+ case QLowEnergyController::ClosingState:
+ {
+ bool emitDisconnect = disconnectSignalRequired;
+ bool emitError = pendingConnect;
+
+ resetController();
+
+ if (emitError)
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+
+ if (emitDisconnect) {
+ Q_Q(QLowEnergyController);
+ emit q->disconnected();
+ }
+ }
+ break;
+ case QLowEnergyController::AdvertisingState:
+ case QLowEnergyController::UnconnectedState:
+ //ignore
+ break;
+ }
+ }
+ }
+ }
+}
+
+void QLowEnergyControllerPrivateBluezDBus::interfacesRemoved(
+ const QDBusObjectPath &objectPath, const QStringList &/*interfaces*/)
+{
+ if (objectPath.path() == device->path()) {
+ resetController();
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ qCWarning(QT_BT_BLUEZ) << "DBus Device1 was removed";
+ setState(QLowEnergyController::UnconnectedState);
+ } else if (objectPath.path() == adapter->path()) {
+ resetController();
+ setError(QLowEnergyController::InvalidBluetoothAdapterError);
+ qCWarning(QT_BT_BLUEZ) << "DBus Adapter was removed";
+ setState(QLowEnergyController::UnconnectedState);
+ }
+}
+
+void QLowEnergyControllerPrivateBluezDBus::resetController()
+{
+ if (managerBluez) {
+ delete managerBluez;
+ managerBluez = nullptr;
+ }
+
+ if (adapter) {
+ delete adapter;
+ adapter = nullptr;
+ }
+
+ if (device) {
+ delete device;
+ device = nullptr;
+ }
+
+ if (deviceMonitor) {
+ delete deviceMonitor;
+ deviceMonitor = nullptr;
+ }
+
+ pendingConnect = pendingDisconnect = disconnectSignalRequired = false;
+}
+
+void QLowEnergyControllerPrivateBluezDBus::connectToDeviceHelper()
+{
+ resetController();
+
+ bool ok = false;
+ const QString hostAdapterPath = findAdapterForAddress(localAdapter, &ok);
+ if (!ok || hostAdapterPath.isEmpty()) {
+ qCWarning(QT_BT_BLUEZ) << "Cannot find suitable bluetooth adapter";
+ setError(QLowEnergyController::InvalidBluetoothAdapterError);
+ return;
+ }
+
+ QScopedPointer<OrgFreedesktopDBusObjectManagerInterface> manager(
+ new OrgFreedesktopDBusObjectManagerInterface(
+ QStringLiteral("org.bluez"), QStringLiteral("/"),
+ QDBusConnection::systemBus()));
+
+ QDBusPendingReply<ManagedObjectList> reply = manager->GetManagedObjects();
+ reply.waitForFinished();
+ if (reply.isError()) {
+ qCWarning(QT_BT_BLUEZ) << "Cannot enumerate Bluetooth devices for GATT connect";
+ setError(QLowEnergyController::ConnectionError);
+ return;
+ }
+
+ QString devicePath;
+ ManagedObjectList managedObjectList = reply.value();
+ for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) {
+ const InterfaceList &ifaceList = it.value();
+
+ for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) {
+ const QString &iface = jt.key();
+ const QVariantMap &ifaceValues = jt.value();
+
+ if (iface == QStringLiteral("org.bluez.Device1")) {
+ if (remoteDevice.toString() == ifaceValues.value(QStringLiteral("Address")).toString()) {
+ devicePath = it.key().path();
+ break;
+ }
+ }
+ }
+
+ if (!devicePath.isEmpty())
+ break;
+ }
+
+ if (devicePath.isEmpty()) {
+ qCDebug(QT_BT_BLUEZ) << "Cannot find targeted remote device. "
+ "Re-running device discovery might help";
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ return;
+ }
+
+ managerBluez = manager.take();
+ connect(managerBluez, &OrgFreedesktopDBusObjectManagerInterface::InterfacesRemoved,
+ this, &QLowEnergyControllerPrivateBluezDBus::interfacesRemoved);
+ adapter = new OrgBluezAdapter1Interface(
+ QStringLiteral("org.bluez"), hostAdapterPath,
+ QDBusConnection::systemBus(), this);
+ device = new OrgBluezDevice1Interface(
+ QStringLiteral("org.bluez"), devicePath,
+ QDBusConnection::systemBus(), this);
+ deviceMonitor = new OrgFreedesktopDBusPropertiesInterface(
+ QStringLiteral("org.bluez"), devicePath,
+ QDBusConnection::systemBus(), this);
+ connect(deviceMonitor, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
+ this, &QLowEnergyControllerPrivateBluezDBus::devicePropertiesChanged);
+}
+
void QLowEnergyControllerPrivateBluezDBus::connectToDevice()
{
- qWarning() << "QLowEnergyControllerPrivateBluezDBus::connectToDevice(): Not implemented";
- //setError(QLowEnergyController::UnknownError);
+ qCDebug(QT_BT_BLUEZ) << "QLowEnergyControllerPrivateBluezDBus::connectToDevice()";
+
+ connectToDeviceHelper();
+
+ if (!adapter || !device)
+ return;
+
+ if (!adapter->powered()) {
+ qCWarning(QT_BT_BLUEZ) << "Error: Local adapter is powered off";
+ setError(QLowEnergyController::ConnectionError);
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectingState);
+
+ //Bluez interface is shared among all platform processes
+ //and hence we might be connected already
+ if (device->connected() && device->servicesResolved()) {
+ //connectToDevice is noop
+ disconnectSignalRequired = true;
+ setState(QLowEnergyController::ConnectedState);
+ return;
+ }
+
+ pendingConnect = true;
+
+ QDBusPendingReply<> reply = device->Connect();
+ QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this,
+ [=](QDBusPendingCallWatcher* call) {
+ QDBusPendingReply<> reply = *call;
+ if (reply.isError()) {
+ qCDebug(QT_BT_BLUEZ) << "BTLE_DBUS::connect() failed"
+ << reply.reply().errorName()
+ << reply.reply().errorMessage();
+ bool emitDisconnect = disconnectSignalRequired;
+ resetController();
+ setError(QLowEnergyController::UnknownError);
+ setState(QLowEnergyController::UnconnectedState);
+ if (emitDisconnect) {
+ Q_Q(QLowEnergyController);
+ emit q->disconnected();
+ }
+ } // else -> connected when Connected property is set to true (see devicePropertiesChanged())
+ call->deleteLater();
+ });
}
void QLowEnergyControllerPrivateBluezDBus::disconnectFromDevice()
{
+ setState(QLowEnergyController::ClosingState);
+
+ pendingDisconnect = true;
+ QDBusPendingReply<> reply = device->Disconnect();
+ QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this,
+ [=](QDBusPendingCallWatcher* call) {
+ QDBusPendingReply<> reply = *call;
+ if (reply.isError()) {
+ qCDebug(QT_BT_BLUEZ) << "BTLE_DBUS::disconnect() failed"
+ << reply.reply().errorName()
+ << reply.reply().errorMessage();
+ bool emitDisconnect = disconnectSignalRequired;
+ resetController();
+ setState(QLowEnergyController::UnconnectedState);
+ if (emitDisconnect) {
+ Q_Q(QLowEnergyController);
+ emit q->disconnected();
+ }
+ }
+ call->deleteLater();
+ });
}
void QLowEnergyControllerPrivateBluezDBus::discoverServices()
diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus_p.h b/src/bluetooth/qlowenergycontroller_bluezdbus_p.h
index b10199f5..c4589381 100644
--- a/src/bluetooth/qlowenergycontroller_bluezdbus_p.h
+++ b/src/bluetooth/qlowenergycontroller_bluezdbus_p.h
@@ -55,6 +55,13 @@
#include "qlowenergycontroller.h"
#include "qlowenergycontrollerbase_p.h"
+#include <QtDBus/QDBusObjectPath>
+
+class OrgBluezAdapter1Interface;
+class OrgBluezDevice1Interface;
+class OrgFreedesktopDBusObjectManagerInterface;
+class OrgFreedesktopDBusPropertiesInterface;
+
QT_BEGIN_NAMESPACE
class QLowEnergyControllerPrivateBluezDBus : public QLowEnergyControllerPrivate
@@ -104,8 +111,25 @@ public:
QLowEnergyService *addServiceHelper(const QLowEnergyServiceData &service) override;
- QLowEnergyController::ControllerState state;
- QLowEnergyController::Error error;
+
+private:
+ void connectToDeviceHelper();
+ void resetController();
+
+private slots:
+ void devicePropertiesChanged(const QString &interface, const QVariantMap &changedProperties,
+ const QStringList &/*invalidatedProperties*/);
+ void interfacesRemoved(const QDBusObjectPath &objectPath, const QStringList &interfaces);
+
+private:
+ OrgBluezAdapter1Interface* adapter{};
+ OrgBluezDevice1Interface* device{};
+ OrgFreedesktopDBusObjectManagerInterface* managerBluez{};
+ OrgFreedesktopDBusPropertiesInterface* deviceMonitor{};
+
+ bool pendingConnect = false;
+ bool pendingDisconnect = false;
+ bool disconnectSignalRequired = false;
};
QT_END_NAMESPACE