summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@qt.io>2018-07-25 14:28:22 +0200
committerAlex Blasche <alexander.blasche@qt.io>2018-08-09 08:55:04 +0000
commit80bea1f32b92c70c21c96762e31c726cb49e180f (patch)
tree4cc8558c45986359a51756b4959756c712ca5332
parentf1d00a83d12f27c37c20b6138f00ff82960018c1 (diff)
Implement QBluetoothSocket Client support for Bluez5 DBus
The new code is not yet enabled in QBluetoothSocket because the server side implementation is still missing. Task-number: QTBUG-68550 Change-Id: I2f94dac9f7665c8d4ba5d675e91c5ab81af8504a Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--src/bluetooth/bluetooth.pro2
-rw-r--r--src/bluetooth/bluez/bluez.pri2
-rw-r--r--src/bluetooth/bluez/profile1context.cpp72
-rw-r--r--src/bluetooth/bluez/profile1context_p.h96
-rw-r--r--src/bluetooth/qbluetoothsocket.cpp6
-rw-r--r--src/bluetooth/qbluetoothsocket_bluezdbus.cpp451
-rw-r--r--src/bluetooth/qbluetoothsocket_bluezdbus_p.h26
7 files changed, 640 insertions, 15 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 884dbebf..72208a87 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -87,7 +87,7 @@ win32 {
qtConfig(bluez) {
QT_PRIVATE = concurrent
- QT_FOR_PRIVATE += dbus
+ QT_FOR_PRIVATE += dbus network
# do not link against QtNetwork but use inline qt_safe_* functions
INCLUDEPATH += $$QT.network_private.includes
diff --git a/src/bluetooth/bluez/bluez.pri b/src/bluetooth/bluez/bluez.pri
index 9fb9242e..4201f104 100644
--- a/src/bluetooth/bluez/bluez.pri
+++ b/src/bluetooth/bluez/bluez.pri
@@ -15,6 +15,7 @@ HEADERS += bluez/manager_p.h \
bluez/device1_bluez5_p.h \
bluez/profilemanager1_p.h \
bluez/profile1_p.h \
+ bluez/profile1context_p.h \
bluez/obex_client1_bluez5_p.h \
bluez/obex_objectpush1_bluez5_p.h \
bluez/obex_transfer1_bluez5_p.h \
@@ -43,6 +44,7 @@ SOURCES += bluez/manager.cpp \
bluez/bluez5_helper.cpp \
bluez/profilemanager1.cpp \
bluez/profile1.cpp \
+ bluez/profile1context.cpp \
bluez/obex_client1_bluez5.cpp \
bluez/obex_objectpush1_bluez5.cpp \
bluez/obex_transfer1_bluez5.cpp \
diff --git a/src/bluetooth/bluez/profile1context.cpp b/src/bluetooth/bluez/profile1context.cpp
new file mode 100644
index 00000000..5eeec56a
--- /dev/null
+++ b/src/bluetooth/bluez/profile1context.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "profile1context_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
+
+OrgBluezProfile1ContextInterface::OrgBluezProfile1ContextInterface(QObject *parent) : QObject(parent)
+{
+}
+
+void OrgBluezProfile1ContextInterface::NewConnection(const QDBusObjectPath &/*remotePath*/,
+ const QDBusUnixFileDescriptor &descriptor,
+ const QVariantMap &/*properties*/)
+{
+ qCDebug(QT_BT_BLUEZ) << "Profile Context: New Connection";
+ emit newConnection(descriptor);
+ setDelayedReply(false);
+}
+
+void OrgBluezProfile1ContextInterface::RequestDisconnection(const QDBusObjectPath &/*remotePath*/)
+{
+ qCDebug(QT_BT_BLUEZ) << "Profile Context: Request Disconnection";
+ setDelayedReply(false);
+}
+
+void OrgBluezProfile1ContextInterface::Release()
+{
+ qCDebug(QT_BT_BLUEZ) << "Profile Context: Release";
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/bluez/profile1context_p.h b/src/bluetooth/bluez/profile1context_p.h
new file mode 100644
index 00000000..bd7b7df3
--- /dev/null
+++ b/src/bluetooth/bluez/profile1context_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROFILECONTEXT_P_H
+#define PROFILECONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtDBus/qdbuscontext.h>
+#include <QtDBus/qdbusextratypes.h>
+#include <QtDBus/qdbusunixfiledescriptor.h>
+
+QT_BEGIN_NAMESPACE
+
+class OrgBluezProfile1ContextInterface : public QObject, protected QDBusContext
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.bluez.Profile1")
+ Q_CLASSINFO("D-Bus Introspection", ""
+" <interface name=\"org.bluez.Profile1\">\n"
+" <method name=\"NewConnection\">\n"
+" <arg direction=\"in\" type=\"o\"/>\n"
+" <arg direction=\"in\" type=\"h\"/>\n"
+" <arg direction=\"in\" type=\"a{sv}\"/>\n"
+" <annotation value=\"QVariantMap\" name=\"org.qtproject.QtDBus.QtTypeName.In2\"/>\n"
+" </method>\n"
+" <method name=\"RequestDisconnection\">\n"
+" <arg direction=\"in\" type=\"o\"/>\n"
+" </method>\n"
+" <method name=\"Release\">\n"
+" <annotation value=\"true\" name=\"org.freedesktop.DBus.Method.NoReply\"/>\n"
+" </method>\n"
+" </interface>\n"
+ "")
+public:
+ explicit OrgBluezProfile1ContextInterface(QObject *parent = nullptr);
+
+Q_SIGNALS:
+ void newConnection(const QDBusUnixFileDescriptor &fd);
+
+public Q_SLOTS:
+ void NewConnection(const QDBusObjectPath &, const QDBusUnixFileDescriptor &,
+ const QVariantMap &);
+ void RequestDisconnection(const QDBusObjectPath &);
+ Q_NOREPLY void Release();
+};
+
+QT_END_NAMESPACE
+
+#endif // PROFILECONTEXT_P_H
diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp
index dae844d7..4bd59095 100644
--- a/src/bluetooth/qbluetoothsocket.cpp
+++ b/src/bluetooth/qbluetoothsocket.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Copyright (C) 2016 BlackBerry Limited. All rights reserved.
** Contact: https://www.qt.io/licensing/
**
@@ -41,6 +41,7 @@
#include "qbluetoothsocket.h"
#if QT_CONFIG(bluez)
#include "qbluetoothsocket_bluez_p.h"
+#include "qbluetoothsocket_bluezdbus_p.h"
#elif defined(QT_ANDROID_BLUETOOTH)
#include "qbluetoothsocket_android_p.h"
#elif defined(QT_WINRT_BLUETOOTH)
@@ -259,6 +260,7 @@ QBluetoothSocket::QBluetoothSocket(QBluetoothServiceInfo::Protocol socketType, Q
{
#if QT_CONFIG(bluez)
d_ptr = new QBluetoothSocketPrivateBluez();
+ //d_ptr = new QBluetoothSocketPrivateBluezDBus();
#elif defined(QT_ANDROID_BLUETOOTH)
d_ptr = new QBluetoothSocketPrivateAndroid();
#elif defined(QT_WINRT_BLUETOOTH)
@@ -282,6 +284,7 @@ QBluetoothSocket::QBluetoothSocket(QObject *parent)
{
#if QT_CONFIG(bluez)
d_ptr = new QBluetoothSocketPrivateBluez();
+ //d_ptr = new QBluetoothSocketPrivateBluezDBus();
#elif defined(QT_ANDROID_BLUETOOTH)
d_ptr = new QBluetoothSocketPrivateAndroid();
#elif defined(QT_WINRT_BLUETOOTH)
@@ -724,6 +727,7 @@ void QBluetoothSocket::close()
d->close();
#ifndef QT_ANDROID_BLUETOOTH
+ // TODO Add return type to d->close() & d->abort() to detect when to emit below signals
//Android closes when the Java event loop comes around
setSocketState(UnconnectedState);
emit readChannelFinished();
diff --git a/src/bluetooth/qbluetoothsocket_bluezdbus.cpp b/src/bluetooth/qbluetoothsocket_bluezdbus.cpp
index bdcc89ef..52193a1a 100644
--- a/src/bluetooth/qbluetoothsocket_bluezdbus.cpp
+++ b/src/bluetooth/qbluetoothsocket_bluezdbus.cpp
@@ -40,10 +40,40 @@
#include "qbluetoothsocket.h"
#include "qbluetoothsocket_bluezdbus_p.h"
+#include "bluez/bluez_data_p.h"
+#include "bluez/bluez5_helper_p.h"
+#include "bluez/adapter1_bluez5_p.h"
+#include "bluez/device1_bluez5_p.h"
+#include "bluez/objectmanager_p.h"
+#include "bluez/profile1_p.h"
+#include "bluez/profile1context_p.h"
+#include "bluez/profilemanager1_p.h"
+
+#include <QtBluetooth/qbluetoothdeviceinfo.h>
+#include <QtBluetooth/qbluetoothserviceinfo.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qrandom.h>
+
+#include <QtNetwork/qlocalsocket.h>
+
+#include <unistd.h>
+
QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
+
+static const QLatin1String profilePathTemplate("/qt/btsocket/%1%2/%3");
+
QBluetoothSocketPrivateBluezDBus::QBluetoothSocketPrivateBluezDBus()
{
secFlags = QBluetooth::NoSecurity;
+
+ profileManager = new OrgBluezProfileManager1Interface(
+ QStringLiteral("org.bluez"),
+ QStringLiteral("/org/bluez"),
+ QDBusConnection::systemBus(),
+ this);
}
QBluetoothSocketPrivateBluezDBus::~QBluetoothSocketPrivateBluezDBus()
@@ -52,71 +82,361 @@ QBluetoothSocketPrivateBluezDBus::~QBluetoothSocketPrivateBluezDBus()
bool QBluetoothSocketPrivateBluezDBus::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
{
- socketType = type;
+ switch (type) {
+ case QBluetoothServiceInfo::UnknownProtocol:
+ break;
+ case QBluetoothServiceInfo::RfcommProtocol:
+ case QBluetoothServiceInfo::L2capProtocol:
+ socketType = type;
+ return true;
+ }
+
return false;
}
-void QBluetoothSocketPrivateBluezDBus::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+void QBluetoothSocketPrivateBluezDBus::connectToServiceHelper(
+ const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
{
+ // TODO Remove when Bluez4 support dropped
+ // Only used by QBluetoothSocketPrivateBluez
Q_UNUSED(openMode);
Q_UNUSED(address);
Q_UNUSED(port);
}
+static QString findRemoteDevicePath(const QBluetoothAddress &address)
+{
+ OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"),
+ QStringLiteral("/"),
+ QDBusConnection::systemBus());
+
+ bool ok = false;
+ const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
+ if (!ok)
+ return QString();
+
+ auto reply = manager.GetManagedObjects();
+ reply.waitForFinished();
+ if (reply.isError())
+ return QString();
+
+ QString remoteDevicePath;
+
+ ManagedObjectList objectList = reply.value();
+ for (ManagedObjectList::const_iterator it = objectList.constBegin();
+ it != objectList.constEnd(); ++it) {
+ const QDBusObjectPath &path = it.key();
+ const InterfaceList &ifaceList = it.value();
+
+ for (InterfaceList::const_iterator ifaceIter = ifaceList.constBegin();
+ ifaceIter != ifaceList.constEnd(); ++ifaceIter) {
+ if (ifaceIter.key() == QStringLiteral("org.bluez.Device1")) {
+ if (path.path().indexOf(adapterPath) != 0)
+ continue; // devices whose path does not start with same path we skip
+
+ OrgBluezDevice1Interface device(QStringLiteral("org.bluez"),
+ path.path(), QDBusConnection::systemBus());
+ if (device.adapter().path() != adapterPath)
+ continue;
+
+ const QBluetoothAddress btAddress(device.address());
+ if (btAddress.isNull() || btAddress != address)
+ continue;
+
+ return path.path();
+ }
+ }
+ }
+
+ return QString();
+}
+
+void QBluetoothSocketPrivateBluezDBus::connectToServiceHelper(
+ const QBluetoothAddress &address, const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+
+ int i = 0;
+ bool success = false;
+ profileUuid = uuid.toString(QUuid::WithoutBraces);
+
+ if (profileContext) {
+ qCDebug(QT_BT_BLUEZ) << "Profile context still active. close socket first.";
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+
+
+ profileContext = new OrgBluezProfile1ContextInterface(this);
+ connect(profileContext, &OrgBluezProfile1ContextInterface::newConnection,
+ this, &QBluetoothSocketPrivateBluezDBus::remoteConnected);
+
+ for (i = 0; i < 10 && !success; i++) {
+ // profile registration might fail in case other service uses same path
+ // try 10 times and otherwise abort
+
+ profilePath = QString(profilePathTemplate).
+ arg(sanitizeNameForDBus(QCoreApplication::applicationName())).
+ arg(QCoreApplication::applicationPid()).
+ arg(QRandomGenerator::global()->generate());
+
+ success = QDBusConnection::systemBus().registerObject(
+ profilePath, profileContext, QDBusConnection::ExportAllSlots);
+ }
+
+ if (!success) {
+ // we could not register the profile
+ qCWarning(QT_BT_BLUEZ) << "Cannot export serial client profile on DBus";
+
+ delete profileContext;
+ profileContext = nullptr;
+
+ errorString = QBluetoothSocket::tr("Cannot export profile on DBus");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+
+ return;
+ }
+
+ QVariantMap profileOptions;
+ profileOptions.insert(QStringLiteral("Role"), QStringLiteral("client"));
+ profileOptions.insert(QStringLiteral("Service"), profileUuid);
+ profileOptions.insert(QStringLiteral("Name"),
+ QStringLiteral("QBluetoothSocket-%1").arg(QCoreApplication::applicationPid()));
+
+ // TODO support more profile parameter
+ // profileOptions.insert(QStringLiteral("Channel"), 0);
+
+ qCDebug(QT_BT_BLUEZ) << "Registering client profile on" << profilePath << "with options:";
+ qCDebug(QT_BT_BLUEZ) << profileOptions;
+ QDBusPendingReply<> reply = profileManager->RegisterProfile(
+ QDBusObjectPath(profilePath),
+ profileUuid,
+ profileOptions);
+ reply.waitForFinished();
+ if (reply.isError()) {
+ qCWarning(QT_BT_BLUEZ) << "Client profile registration failed:"
+ << reply.error().message();
+
+ QDBusConnection::systemBus().unregisterObject(profilePath);
+ errorString = QBluetoothSocket::tr("Cannot register profile on DBus");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+
+ remoteDevicePath = findRemoteDevicePath(address);
+ if (remoteDevicePath.isEmpty()) {
+ qCWarning(QT_BT_BLUEZ) << "Unknown remote device:" << address
+ << "Try device discovery first";
+ clearSocket();
+
+ errorString = QBluetoothSocket::tr("Cannot find remote device");
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ return;
+ }
+
+ OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
+ QDBusConnection::systemBus());
+ reply = device.ConnectProfile(profileUuid);
+ if (reply.isError()) {
+ qCWarning(QT_BT_BLUEZ) << "Cannot connect to profile/service:" << uuid;
+
+ clearSocket();
+
+ errorString = QBluetoothSocket::tr("Cannot connect to remote profile");
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ return;
+ }
+
+ q->setOpenMode(openMode);
+ q->setSocketState(QBluetoothSocket::ConnectingState);
+}
+
void QBluetoothSocketPrivateBluezDBus::connectToService(
const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
{
- Q_UNUSED(openMode);
- Q_UNUSED(service);
+ Q_Q(QBluetoothSocket);
+ QBluetoothUuid targetService;
+
+ targetService = service.serviceUuid();
+ if (targetService.isNull()) {
+ // Do we have serialport service class?
+ if (service.serviceClassUuids().contains(QBluetoothUuid::SerialPort))
+ targetService = QBluetoothUuid::SerialPort;
+ }
+
+ if (targetService.isNull()) {
+ qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
+ << "or SerialPort service class uuid";
+ errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ connectToService(service.device().address(), targetService, openMode);
}
void QBluetoothSocketPrivateBluezDBus::connectToService(
const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
{
- Q_UNUSED(openMode);
- Q_UNUSED(address);
- Q_UNUSED(uuid);
+ Q_Q(QBluetoothSocket);
+
+ if (address.isNull()) {
+ qCWarning(QT_BT_BLUEZ) << "Invalid address to remote address passed.";
+ errorString = QBluetoothSocket::tr("Invalid Bluetooth address passed to connectToService()");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ if (uuid.isNull()) {
+ qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
+ << "or SerialPort service class uuid";
+ errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ if (q->state() != QBluetoothSocket::UnconnectedState) {
+ qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService called on busy socket";
+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return;
+ }
+
+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
+ qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService cannot "
+ "connect with 'UnknownProtocol' (type provided by given service)";
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ if (!ensureNativeSocket(q->socketType())) {
+ errorString = QBluetoothSocket::tr("Socket type not supported");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+ connectToServiceHelper(address, uuid, openMode);
}
void QBluetoothSocketPrivateBluezDBus::connectToService(
const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
{
- Q_UNUSED(openMode);
- Q_UNUSED(address);
+
Q_UNUSED(port);
+ Q_UNUSED(address);
+ Q_UNUSED(openMode);
+ Q_Q(QBluetoothSocket);
+
+ errorString = tr("Connecting to port is not supported via Bluez DBus");
+ q->setSocketError(QBluetoothSocket::ServiceNotFoundError);
+ qCWarning(QT_BT_BLUEZ) << "Connecting to port is not supported (Uuid required)";
}
void QBluetoothSocketPrivateBluezDBus::abort()
{
+ if (localSocket) {
+ localSocket->close();
+ //TODO delayed disconnected() not yet implemented
+ } else {
+ // delayed disconnected not needed
+ clearSocket();
+ }
}
QString QBluetoothSocketPrivateBluezDBus::localName() const
{
- return QString();
+ bool ok = false;
+ const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
+ if (!ok)
+ return QString();
+
+ OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
+ QDBusConnection::systemBus());
+ return QString(adapter.alias());
}
QBluetoothAddress QBluetoothSocketPrivateBluezDBus::localAddress() const
{
- return QBluetoothAddress();
+ bool ok = false;
+ const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
+ if (!ok)
+ return QBluetoothAddress();
+
+ OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
+ QDBusConnection::systemBus());
+ return QBluetoothAddress(adapter.address());
}
quint16 QBluetoothSocketPrivateBluezDBus::localPort() const
{
+ int descriptor = -1;
+
+ if (localSocket)
+ descriptor = int(localSocket->socketDescriptor());
+ if (descriptor == -1)
+ return 0;
+
+ if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
+ sockaddr_rc addr;
+ socklen_t addrLength = sizeof(addr);
+
+ if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
+ return (addr.rc_channel);
+ } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
+ sockaddr_l2 addr;
+ socklen_t addrLength = sizeof(addr);
+
+ if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
+ return addr.l2_psm;
+ }
+
return 0;
}
QString QBluetoothSocketPrivateBluezDBus::peerName() const
{
- return QString();
+ if (remoteDevicePath.isEmpty())
+ return QString();
+
+ OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
+ QDBusConnection::systemBus());
+ return device.alias();
}
QBluetoothAddress QBluetoothSocketPrivateBluezDBus::peerAddress() const
{
- return QBluetoothAddress();
+ if (remoteDevicePath.isEmpty())
+ return QBluetoothAddress();
+
+ OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
+ QDBusConnection::systemBus());
+ return QBluetoothAddress(device.address());
}
quint16 QBluetoothSocketPrivateBluezDBus::peerPort() const
{
+ int descriptor = -1;
+
+ if (localSocket)
+ descriptor = int(localSocket->socketDescriptor());
+ if (descriptor == -1)
+ return 0;
+
+ if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
+ sockaddr_rc addr;
+ socklen_t addrLength = sizeof(addr);
+
+ if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
+ return addr.rc_channel;
+ } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
+ sockaddr_l2 addr;
+ socklen_t addrLength = sizeof(addr);
+
+ if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
+ return addr.l2_psm;
+ }
+
return 0;
}
@@ -132,6 +452,10 @@ qint64 QBluetoothSocketPrivateBluezDBus::writeData(const char *data, qint64 maxS
q->setSocketError(QBluetoothSocket::OperationError);
return -1;
}
+
+ if (localSocket)
+ return localSocket->write(data, maxSize);
+
return -1;
}
@@ -148,11 +472,15 @@ qint64 QBluetoothSocketPrivateBluezDBus::readData(char *data, qint64 maxSize)
return -1;
}
+ if (localSocket)
+ return localSocket->read(data, maxSize);
+
return -1;
}
void QBluetoothSocketPrivateBluezDBus::close()
{
+ abort();
}
bool QBluetoothSocketPrivateBluezDBus::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
@@ -167,16 +495,113 @@ bool QBluetoothSocketPrivateBluezDBus::setSocketDescriptor(int socketDescriptor,
qint64 QBluetoothSocketPrivateBluezDBus::bytesAvailable() const
{
+ if (localSocket)
+ return localSocket->bytesAvailable();
+
return 0;
}
bool QBluetoothSocketPrivateBluezDBus::canReadLine() const
{
+ if (localSocket)
+ return localSocket->canReadLine();
+
return false;
}
qint64 QBluetoothSocketPrivateBluezDBus::bytesToWrite() const
{
+ if (localSocket)
+ return localSocket->bytesToWrite();
+
return 0;
}
+
+void QBluetoothSocketPrivateBluezDBus::remoteConnected(const QDBusUnixFileDescriptor &fd)
+{
+ Q_Q(QBluetoothSocket);
+
+ int descriptor = ::dup(fd.fileDescriptor());
+ localSocket = new QLocalSocket(this);
+ bool success = localSocket->setSocketDescriptor(
+ descriptor, QLocalSocket::ConnectedState, q->openMode());
+ if (!success || !localSocket->isValid()) {
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ delete localSocket;
+ localSocket = nullptr;
+ } else {
+ connect(localSocket, &QLocalSocket::readyRead,
+ q, &QBluetoothSocket::readyRead);
+ connect(localSocket, &QLocalSocket::stateChanged,
+ this, &QBluetoothSocketPrivateBluezDBus::socketStateChanged);
+
+ q->setSocketState(QBluetoothSocket::ConnectedState);
+ emit q->connected();
+ }
+}
+
+void QBluetoothSocketPrivateBluezDBus::socketStateChanged(QLocalSocket::LocalSocketState newState)
+{
+ Q_Q(QBluetoothSocket);
+
+ switch (newState) {
+ case QLocalSocket::ClosingState:
+ q->setSocketState(QBluetoothSocket::ClosingState);
+ break;
+ case QLocalSocket::UnconnectedState:
+ clearSocket();
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+
+ emit q->readChannelFinished();
+ emit q->disconnected();
+ break;
+ default:
+ // ConnectingState and ConnectedState not mapped
+ // (already set at the time when the socket is created)
+ break;
+ }
+}
+
+void QBluetoothSocketPrivateBluezDBus::clearSocket()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (profilePath.isEmpty())
+ return;
+
+ qCDebug(QT_BT_BLUEZ) << "Clearing profile called for" << profilePath;
+
+ if (localSocket) {
+ localSocket->close();
+ localSocket->deleteLater();
+ localSocket = nullptr;
+ }
+
+ if (q->state() == QBluetoothSocket::ConnectedState) {
+ OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
+ QDBusConnection::systemBus());
+ auto reply = device.DisconnectProfile(profileUuid);
+ reply.waitForFinished();
+ if (reply.isError()) {
+ qCWarning(QT_BT_BLUEZ) << "Disconnect profile failed:"
+ << reply.error().message();
+ }
+ }
+
+ QDBusPendingReply<> reply = profileManager->UnregisterProfile(QDBusObjectPath(profilePath));
+ reply.waitForFinished();
+ if (reply.isError())
+ qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
+
+ QDBusConnection::systemBus().unregisterObject(profilePath);
+
+ if (profileContext) {
+ delete profileContext;
+ profileContext = nullptr;
+ }
+
+ remoteDevicePath.clear();
+ profileUuid.clear();
+ profilePath.clear();
+}
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket_bluezdbus_p.h b/src/bluetooth/qbluetoothsocket_bluezdbus_p.h
index c7279555..4d1a272e 100644
--- a/src/bluetooth/qbluetoothsocket_bluezdbus_p.h
+++ b/src/bluetooth/qbluetoothsocket_bluezdbus_p.h
@@ -53,8 +53,17 @@
#include "qbluetoothsocketbase_p.h"
+#include <QtDBus/qdbusunixfiledescriptor.h>
+
+#include <QtNetwork/qlocalsocket.h>
+
+class OrgBluezProfileManager1Interface;
+
QT_BEGIN_NAMESPACE
+class QLocalSocket;
+class OrgBluezProfile1ContextInterface;
+
class QBluetoothSocketPrivateBluezDBus final: public QBluetoothSocketBasePrivate
{
Q_OBJECT
@@ -66,6 +75,9 @@ public:
void connectToServiceHelper(const QBluetoothAddress &address,
quint16 port,
QIODevice::OpenMode openMode) override;
+ void connectToServiceHelper(const QBluetoothAddress &address,
+ const QBluetoothUuid &uuid,
+ QIODevice::OpenMode openMode);
void connectToService(const QBluetoothServiceInfo &service,
QIODevice::OpenMode openMode) override;
@@ -97,6 +109,20 @@ public:
qint64 bytesAvailable() const override;
bool canReadLine() const override;
qint64 bytesToWrite() const override;
+
+private:
+ void remoteConnected(const QDBusUnixFileDescriptor &fd);
+ void socketStateChanged(QLocalSocket::LocalSocketState newState);
+
+ void clearSocket();
+
+private:
+ OrgBluezProfileManager1Interface *profileManager = nullptr;
+ OrgBluezProfile1ContextInterface *profileContext = nullptr;
+ QString remoteDevicePath;
+ QString profileUuid;
+ QString profilePath;
+ QLocalSocket *localSocket = nullptr;
};
QT_END_NAMESPACE