diff options
author | Alex Blasche <alexander.blasche@digia.com> | 2014-07-03 09:23:25 +0200 |
---|---|---|
committer | Alex Blasche <alexander.blasche@digia.com> | 2014-07-03 09:23:25 +0200 |
commit | f78b5f54d8c7536257629ad39bc300e233584949 (patch) | |
tree | e919f3f79a97e14fe927e840b2ad845d5ee92ee3 /src | |
parent | d2c043d9bb1172f37b3d9051b0483c4a0f3a5d05 (diff) | |
parent | cb6bdcb3fb94a38613304b60724e0cd02962cd14 (diff) |
Merge branch 'dev' into btle
Conflicts:
src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
Change-Id: Ib960b9e8d8800af84d2094bdf26c2652d4c81dba
Diffstat (limited to 'src')
30 files changed, 1062 insertions, 213 deletions
diff --git a/src/bluetooth/android/jni_android.cpp b/src/bluetooth/android/jni_android.cpp index ebc46a7c..b7221867 100644 --- a/src/bluetooth/android/jni_android.cpp +++ b/src/bluetooth/android/jni_android.cpp @@ -93,7 +93,7 @@ QAndroidJniObject valueForStaticField(JavaNames javaName, JavaNames javaFieldNam case JavaNames::BluetoothDevice: className = javaBluetoothDeviceClassName; break; default: - qWarning(QT_BT_ANDROID) << "Unknown java class name passed to valueForStaticField():" << javaName; + qCWarning(QT_BT_ANDROID) << "Unknown java class name passed to valueForStaticField():" << javaName; return QAndroidJniObject(); } @@ -132,7 +132,7 @@ QAndroidJniObject valueForStaticField(JavaNames javaName, JavaNames javaFieldNam case JavaNames::ExtraUuid: fieldName = javaExtraUuid; break; default: - qWarning(QT_BT_ANDROID) << "Unknown java field name passed to valueForStaticField():" << javaFieldName; + qCWarning(QT_BT_ANDROID) << "Unknown java field name passed to valueForStaticField():" << javaFieldName; return QAndroidJniObject(); } diff --git a/src/bluetooth/bluez/bluez.pri b/src/bluetooth/bluez/bluez.pri index 0fa4e5c9..e1cdb58a 100644 --- a/src/bluetooth/bluez/bluez.pri +++ b/src/bluetooth/bluez/bluez.pri @@ -26,7 +26,11 @@ HEADERS += bluez/manager_p.h \ bluez/objectmanager_p.h \ bluez/properties_p.h \ bluez/adapter1_bluez5_p.h \ - bluez/device1_bluez5_p.h + bluez/device1_bluez5_p.h \ + bluez/profile1_p.h \ + bluez/obex_client1_bluez5_p.h \ + bluez/obex_objectpush1_bluez5_p.h \ + bluez/obex_transfer1_bluez5_p.h SOURCES += bluez/manager.cpp \ @@ -44,4 +48,8 @@ SOURCES += bluez/manager.cpp \ bluez/properties.cpp \ bluez/adapter1_bluez5.cpp \ bluez/device1_bluez5.cpp \ - bluez/bluez5_helper.cpp + bluez/bluez5_helper.cpp \ + bluez/profile1.cpp \ + bluez/obex_client1_bluez5.cpp \ + bluez/obex_objectpush1_bluez5.cpp \ + bluez/obex_transfer1_bluez5.cpp diff --git a/src/bluetooth/bluez/bluez5_helper.cpp b/src/bluetooth/bluez/bluez5_helper.cpp index c995d948..18ab22b1 100644 --- a/src/bluetooth/bluez/bluez5_helper.cpp +++ b/src/bluetooth/bluez/bluez5_helper.cpp @@ -457,4 +457,65 @@ QByteArray parseSdpRecord(sdp_record_t *record) return xmlOutput; } + +/*! + Finds the path for the local adapter with \a wantedAddress or an empty string + if no local adapter with the given address can be found. + If \a wantedAddress is \c null it returns the first/default adapter or an empty + string if none is available. + + If \a ok is false the lookup was aborted due to a dbus error and this function + returns an empty string. + */ +QString findAdapterForAddress(const QBluetoothAddress &wantedAddress, bool *ok = 0) +{ + OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"), + QStringLiteral("/"), + QDBusConnection::systemBus()); + + QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects(); + reply.waitForFinished(); + if (reply.isError()) { + if (ok) + *ok = false; + + return QString(); + } + + typedef QPair<QString, QBluetoothAddress> AddressForPathType; + QList<AddressForPathType> localAdapters; + + foreach (const QDBusObjectPath &path, reply.value().keys()) { + const InterfaceList ifaceList = reply.value().value(path); + foreach (const QString &iface, ifaceList.keys()) { + if (iface == QStringLiteral("org.bluez.Adapter1")) { + AddressForPathType pair; + pair.first = path.path(); + pair.second = QBluetoothAddress(ifaceList.value(iface).value( + QStringLiteral("Address")).toString()); + if (!pair.second.isNull()) + localAdapters.append(pair); + break; + } + } + } + + if (ok) + *ok = true; + + if (localAdapters.isEmpty()) + return QString(); // -> no local adapter found + + if (wantedAddress.isNull()) + return localAdapters.front().first; // -> return first found adapter + + foreach (const AddressForPathType &pair, localAdapters) { + if (pair.second == wantedAddress) + return pair.first; // -> found local adapter with wanted address + } + + return QString(); // nothing matching found +} + + QT_END_NAMESPACE diff --git a/src/bluetooth/bluez/bluez5_helper_p.h b/src/bluetooth/bluez/bluez5_helper_p.h index 2429c09c..36595f9c 100644 --- a/src/bluetooth/bluez/bluez5_helper_p.h +++ b/src/bluetooth/bluez/bluez5_helper_p.h @@ -45,6 +45,7 @@ #include <QtCore/QObject> #include <QtDBus/QtDBus> #include <bluetooth/sdp.h> +#include <QtBluetooth/QBluetoothAddress> typedef QMap<QString, QVariantMap> InterfaceList; typedef QMap<QDBusObjectPath, InterfaceList> ManagedObjectList; @@ -57,6 +58,7 @@ QT_BEGIN_NAMESPACE bool isBluez5(); QByteArray parseSdpRecord(sdp_record_t *record); +QString findAdapterForAddress(const QBluetoothAddress &wantedAddress, bool *ok); class QtBluezDiscoveryManagerPrivate; class QtBluezDiscoveryManager : public QObject diff --git a/src/bluetooth/bluez/obex_client1_bluez5.cpp b/src/bluetooth/bluez/obex_client1_bluez5.cpp new file mode 100644 index 00000000..fe0fd1f3 --- /dev/null +++ b/src/bluetooth/bluez/obex_client1_bluez5.cpp @@ -0,0 +1,26 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.Client1.xml -p asd + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "obex_client1_bluez5_p.h" + +/* + * Implementation of interface class OrgBluezObexClient1Interface + */ + +OrgBluezObexClient1Interface::OrgBluezObexClient1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +OrgBluezObexClient1Interface::~OrgBluezObexClient1Interface() +{ +} + diff --git a/src/bluetooth/bluez/obex_client1_bluez5_p.h b/src/bluetooth/bluez/obex_client1_bluez5_p.h new file mode 100644 index 00000000..f8f0f89a --- /dev/null +++ b/src/bluetooth/bluez/obex_client1_bluez5_p.h @@ -0,0 +1,63 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.Client1.xml -p asd + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef ASD_H_1399976174 +#define ASD_H_1399976174 + +#include <QtCore/QObject> +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtDBus/QtDBus> + +/* + * Proxy class for interface org.bluez.obex.Client1 + */ +class OrgBluezObexClient1Interface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.bluez.obex.Client1"; } + +public: + OrgBluezObexClient1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); + + ~OrgBluezObexClient1Interface(); + +public Q_SLOTS: // METHODS + inline QDBusPendingReply<QDBusObjectPath> CreateSession(const QString &destination, const QVariantMap &args) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(destination) << QVariant::fromValue(args); + return asyncCallWithArgumentList(QLatin1String("CreateSession"), argumentList); + } + + inline QDBusPendingReply<> RemoveSession(const QDBusObjectPath &session) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(session); + return asyncCallWithArgumentList(QLatin1String("RemoveSession"), argumentList); + } + +Q_SIGNALS: // SIGNALS +}; + +namespace org { + namespace bluez { + namespace obex { + typedef ::OrgBluezObexClient1Interface Client1; + } + } +} +#endif diff --git a/src/bluetooth/bluez/obex_objectpush1_bluez5.cpp b/src/bluetooth/bluez/obex_objectpush1_bluez5.cpp new file mode 100644 index 00000000..637a3a62 --- /dev/null +++ b/src/bluetooth/bluez/obex_objectpush1_bluez5.cpp @@ -0,0 +1,26 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.obex.ObjectPush1.xml -p obex_objectpush1_bluez5 + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "obex_objectpush1_bluez5_p.h" + +/* + * Implementation of interface class OrgBluezObexObjectPush1Interface + */ + +OrgBluezObexObjectPush1Interface::OrgBluezObexObjectPush1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +OrgBluezObexObjectPush1Interface::~OrgBluezObexObjectPush1Interface() +{ +} + diff --git a/src/bluetooth/bluez/obex_objectpush1_bluez5_p.h b/src/bluetooth/bluez/obex_objectpush1_bluez5_p.h new file mode 100644 index 00000000..f8adcab4 --- /dev/null +++ b/src/bluetooth/bluez/obex_objectpush1_bluez5_p.h @@ -0,0 +1,100 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.obex.ObjectPush1.xml -p obex_objectpush1_bluez5 + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef OBEX_OBJECTPUSH1_BLUEZ5_H_1399989318 +#define OBEX_OBJECTPUSH1_BLUEZ5_H_1399989318 + +#include <QtCore/QObject> +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtDBus/QtDBus> + +/* + * Proxy class for interface org.bluez.obex.ObjectPush1 + */ +class OrgBluezObexObjectPush1Interface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.bluez.obex.ObjectPush1"; } + +public: + OrgBluezObexObjectPush1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); + + ~OrgBluezObexObjectPush1Interface(); + +public Q_SLOTS: // METHODS + inline QDBusPendingReply<QDBusObjectPath, QVariantMap> ExchangeBusinessCards(const QString &clientfile, const QString &targetfile) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(clientfile) << QVariant::fromValue(targetfile); + return asyncCallWithArgumentList(QLatin1String("ExchangeBusinessCards"), argumentList); + } + inline QDBusReply<QDBusObjectPath> ExchangeBusinessCards(const QString &clientfile, const QString &targetfile, QVariantMap &properties) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(clientfile) << QVariant::fromValue(targetfile); + QDBusMessage reply = callWithArgumentList(QDBus::Block, QLatin1String("ExchangeBusinessCards"), argumentList); + if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 2) { + properties = qdbus_cast<QVariantMap>(reply.arguments().at(1)); + } + return reply; + } + + inline QDBusPendingReply<QDBusObjectPath, QVariantMap> PullBusinessCard(const QString &targetfile) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(targetfile); + return asyncCallWithArgumentList(QLatin1String("PullBusinessCard"), argumentList); + } + inline QDBusReply<QDBusObjectPath> PullBusinessCard(const QString &targetfile, QVariantMap &properties) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(targetfile); + QDBusMessage reply = callWithArgumentList(QDBus::Block, QLatin1String("PullBusinessCard"), argumentList); + if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 2) { + properties = qdbus_cast<QVariantMap>(reply.arguments().at(1)); + } + return reply; + } + + inline QDBusPendingReply<QDBusObjectPath, QVariantMap> SendFile(const QString &sourcefile) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(sourcefile); + return asyncCallWithArgumentList(QLatin1String("SendFile"), argumentList); + } + inline QDBusReply<QDBusObjectPath> SendFile(const QString &sourcefile, QVariantMap &properties) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(sourcefile); + QDBusMessage reply = callWithArgumentList(QDBus::Block, QLatin1String("SendFile"), argumentList); + if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 2) { + properties = qdbus_cast<QVariantMap>(reply.arguments().at(1)); + } + return reply; + } + +Q_SIGNALS: // SIGNALS +}; + +namespace org { + namespace bluez { + namespace obex { + typedef ::OrgBluezObexObjectPush1Interface ObjectPush1; + } + } +} +#endif diff --git a/src/bluetooth/bluez/obex_transfer1_bluez5.cpp b/src/bluetooth/bluez/obex_transfer1_bluez5.cpp new file mode 100644 index 00000000..51f2a013 --- /dev/null +++ b/src/bluetooth/bluez/obex_transfer1_bluez5.cpp @@ -0,0 +1,26 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.obex.Transfer1.xml -p obex_transfer1_bluez5 + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "obex_transfer1_bluez5_p.h" + +/* + * Implementation of interface class OrgBluezObexTransfer1Interface + */ + +OrgBluezObexTransfer1Interface::OrgBluezObexTransfer1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +OrgBluezObexTransfer1Interface::~OrgBluezObexTransfer1Interface() +{ +} + diff --git a/src/bluetooth/bluez/obex_transfer1_bluez5_p.h b/src/bluetooth/bluez/obex_transfer1_bluez5_p.h new file mode 100644 index 00000000..46b86248 --- /dev/null +++ b/src/bluetooth/bluez/obex_transfer1_bluez5_p.h @@ -0,0 +1,91 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.obex.Transfer1.xml -p obex_transfer1_bluez5 + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef OBEX_TRANSFER1_BLUEZ5_H_1400058158 +#define OBEX_TRANSFER1_BLUEZ5_H_1400058158 + +#include <QtCore/QObject> +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtDBus/QtDBus> + +/* + * Proxy class for interface org.bluez.obex.Transfer1 + */ +class OrgBluezObexTransfer1Interface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.bluez.obex.Transfer1"; } + +public: + OrgBluezObexTransfer1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); + + ~OrgBluezObexTransfer1Interface(); + + Q_PROPERTY(QString Filename READ filename) + inline QString filename() const + { return qvariant_cast< QString >(property("Filename")); } + + Q_PROPERTY(QString Name READ name) + inline QString name() const + { return qvariant_cast< QString >(property("Name")); } + + Q_PROPERTY(QDBusObjectPath Session READ session) + inline QDBusObjectPath session() const + { return qvariant_cast< QDBusObjectPath >(property("Session")); } + + Q_PROPERTY(qulonglong Size READ size) + inline qulonglong size() const + { return qvariant_cast< qulonglong >(property("Size")); } + + Q_PROPERTY(QString Status READ status) + inline QString status() const + { return qvariant_cast< QString >(property("Status")); } + + Q_PROPERTY(qulonglong Transferred READ transferred) + inline qulonglong transferred() const + { return qvariant_cast< qulonglong >(property("Transferred")); } + +public Q_SLOTS: // METHODS + inline QDBusPendingReply<> Cancel() + { + QList<QVariant> argumentList; + return asyncCallWithArgumentList(QLatin1String("Cancel"), argumentList); + } + + inline QDBusPendingReply<> Resume() + { + QList<QVariant> argumentList; + return asyncCallWithArgumentList(QLatin1String("Resume"), argumentList); + } + + inline QDBusPendingReply<> Suspend() + { + QList<QVariant> argumentList; + return asyncCallWithArgumentList(QLatin1String("Suspend"), argumentList); + } + +Q_SIGNALS: // SIGNALS +}; + +namespace org { + namespace bluez { + namespace obex { + typedef ::OrgBluezObexTransfer1Interface Transfer1; + } + } +} +#endif diff --git a/src/bluetooth/bluez/org.bluez.Client1.xml b/src/bluetooth/bluez/org.bluez.Client1.xml new file mode 100644 index 00000000..8a5a25c1 --- /dev/null +++ b/src/bluetooth/bluez/org.bluez.Client1.xml @@ -0,0 +1,15 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.bluez.obex.Client1"> + <method name="CreateSession"> + <arg name="destination" type="s" direction="in"/> + <arg name="args" type="a{sv}" direction="in"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/> + <arg name="session" type="o" direction="out"/> + </method> + <method name="RemoveSession"> + <arg name="session" type="o" direction="in"/> + </method> + </interface> +</node> diff --git a/src/bluetooth/bluez/org.bluez.ProfileManager1.xml b/src/bluetooth/bluez/org.bluez.ProfileManager1.xml new file mode 100644 index 00000000..54c9fd0e --- /dev/null +++ b/src/bluetooth/bluez/org.bluez.ProfileManager1.xml @@ -0,0 +1,16 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.bluez.ProfileManager1"> + <method name="RegisterProfile"> + <arg name="profile" type="o" direction="in"/> + <arg name="UUID" type="s" direction="in"/> + <arg name="options" type="a{sv}" direction="in"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/> + </method> + <method name="UnregisterProfile"> + <arg name="profile" type="o" direction="in"/> + </method> + </interface> +</node> + diff --git a/src/bluetooth/bluez/org.bluez.obex.ObjectPush1.xml b/src/bluetooth/bluez/org.bluez.obex.ObjectPush1.xml new file mode 100644 index 00000000..651f21a5 --- /dev/null +++ b/src/bluetooth/bluez/org.bluez.obex.ObjectPush1.xml @@ -0,0 +1,25 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.bluez.obex.ObjectPush1"> + <method name="SendFile"> + <arg name="sourcefile" type="s" direction="in"/> + <arg name="transfer" type="o" direction="out"/> + <arg name="properties" type="a{sv}" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> + </method> + <method name="PullBusinessCard"> + <arg name="targetfile" type="s" direction="in"/> + <arg name="transfer" type="o" direction="out"/> + <arg name="properties" type="a{sv}" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> + </method> + <method name="ExchangeBusinessCards"> + <arg name="clientfile" type="s" direction="in"/> + <arg name="targetfile" type="s" direction="in"/> + <arg name="transfer" type="o" direction="out"/> + <arg name="properties" type="a{sv}" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> + </method> + </interface> +</node> diff --git a/src/bluetooth/bluez/org.bluez.obex.Transfer1.xml b/src/bluetooth/bluez/org.bluez.obex.Transfer1.xml new file mode 100644 index 00000000..4ee76f7d --- /dev/null +++ b/src/bluetooth/bluez/org.bluez.obex.Transfer1.xml @@ -0,0 +1,16 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.bluez.obex.Transfer1"> + <method name="Suspend"/> + <method name="Resume"/> + <method name="Cancel"/> + <property name="Status" type="s" access="read"/> + <property name="Name" type="s" access="read"/> + <property name="Size" type="t" access="read"/> + <property name="Filename" type="s" access="read"/> + <property name="Transferred" type="t" access="read"/> + <property name="Session" type="o" access="read"/> + </interface> +</node> + diff --git a/src/bluetooth/bluez/profile1.cpp b/src/bluetooth/bluez/profile1.cpp new file mode 100644 index 00000000..2cf15d7e --- /dev/null +++ b/src/bluetooth/bluez/profile1.cpp @@ -0,0 +1,26 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp -p profile1 org.bluez.ProfileManager1.xml + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "profile1_p.h" + +/* + * Implementation of interface class OrgBluezProfileManager1Interface + */ + +OrgBluezProfileManager1Interface::OrgBluezProfileManager1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +OrgBluezProfileManager1Interface::~OrgBluezProfileManager1Interface() +{ +} + diff --git a/src/bluetooth/bluez/profile1_p.h b/src/bluetooth/bluez/profile1_p.h new file mode 100644 index 00000000..d94f1916 --- /dev/null +++ b/src/bluetooth/bluez/profile1_p.h @@ -0,0 +1,61 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp -p profile1 org.bluez.ProfileManager1.xml + * + * qdbusxml2cpp is Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef PROFILE1_H_1400142085 +#define PROFILE1_H_1400142085 + +#include <QtCore/QObject> +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtDBus/QtDBus> + +/* + * Proxy class for interface org.bluez.ProfileManager1 + */ +class OrgBluezProfileManager1Interface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.bluez.ProfileManager1"; } + +public: + OrgBluezProfileManager1Interface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); + + ~OrgBluezProfileManager1Interface(); + +public Q_SLOTS: // METHODS + inline QDBusPendingReply<> RegisterProfile(const QDBusObjectPath &profile, const QString &UUID, const QVariantMap &options) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(profile) << QVariant::fromValue(UUID) << QVariant::fromValue(options); + return asyncCallWithArgumentList(QLatin1String("RegisterProfile"), argumentList); + } + + inline QDBusPendingReply<> UnregisterProfile(const QDBusObjectPath &profile) + { + QList<QVariant> argumentList; + argumentList << QVariant::fromValue(profile); + return asyncCallWithArgumentList(QLatin1String("UnregisterProfile"), argumentList); + } + +Q_SIGNALS: // SIGNALS +}; + +namespace org { + namespace bluez { + typedef ::OrgBluezProfileManager1Interface ProfileManager1; + } +} +#endif diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h index 45615e74..89158c53 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h @@ -79,6 +79,7 @@ public: QObject *parent = 0); ~QBluetoothDeviceDiscoveryAgent(); + // TODO Remove inquiry type in Qt 6 -> not really used anywhere QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType() const; void setInquiryType(QBluetoothDeviceDiscoveryAgent::InquiryType type); diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp index f70c3e64..ed09d598 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp @@ -186,75 +186,52 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startBluez5() { Q_Q(QBluetoothDeviceDiscoveryAgent); - QDBusPendingReply<ManagedObjectList> reply = managerBluez5->GetManagedObjects(); - reply.waitForFinished(); - if (reply.isError()) { - errorString = reply.error().message(); - lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; - emit q->error(lastError); - return; - } - - - OrgBluezAdapter1Interface *tempAdapter = 0; - QMap<QString, QVariantMap> devicesForAdapter; // dbus path for devices for matching adapter - - foreach (const QDBusObjectPath &path, reply.value().keys()) { - const InterfaceList ifaceList = reply.value().value(path); - foreach (const QString &iface, ifaceList.keys()) { - if (iface == QStringLiteral("org.bluez.Adapter1")) { - if (tempAdapter) - continue; - - if (m_adapterAddress.isNull()) { - // use the first found adapter as default - tempAdapter = new OrgBluezAdapter1Interface(QStringLiteral("org.bluez"), - path.path(), - QDBusConnection::systemBus()); - } else { - const QString addressString = ifaceList.value(iface). - value(QStringLiteral("Address")).toString(); - if (m_adapterAddress == QBluetoothAddress(addressString)) { - tempAdapter = new OrgBluezAdapter1Interface( - QStringLiteral("org.bluez"), - path.path(), - QDBusConnection::systemBus()); - } - } - } else if (iface == QStringLiteral("org.bluez.Device1")) { - devicesForAdapter.insert(path.path(), ifaceList.value(iface)); - } - } - } - if (!tempAdapter) { - qCDebug(QT_BT_BLUEZ) << "Cannot find Bluez 5 adapter for device search"; + bool ok = false; + const QString adapterPath = findAdapterForAddress(m_adapterAddress, &ok); + if (!ok || adapterPath.isEmpty()) { + qCWarning(QT_BT_BLUEZ) << "Cannot find Bluez 5 adapter for device search" << ok; lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; errorString = QBluetoothDeviceDiscoveryAgent::tr("Cannot find valid Bluetooth adapter."); q->error(lastError); return; } - if (!tempAdapter->powered()) { + adapterBluez5 = new OrgBluezAdapter1Interface(QStringLiteral("org.bluez"), + adapterPath, + QDBusConnection::systemBus()); + + if (!adapterBluez5->powered()) { qCDebug(QT_BT_BLUEZ) << "Aborting device discovery due to offline Bluetooth Adapter"; lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError; errorString = QBluetoothDeviceDiscoveryAgent::tr("Device is powered off"); - delete tempAdapter; + delete adapterBluez5; + adapterBluez5 = 0; emit q->error(lastError); return; } - adapterBluez5 = tempAdapter; + QtBluezDiscoveryManager::instance()->registerDiscoveryInterest(adapterBluez5->path()); QObject::connect(QtBluezDiscoveryManager::instance(), SIGNAL(discoveryInterrupted(QString)), q, SLOT(_q_discoveryInterrupted(QString))); // collect initial set of information - foreach (const QString &path, devicesForAdapter.keys()) { - if (path.indexOf(adapterBluez5->path()) != 0) - continue; //devices path doesnt start with same path as adapter + QDBusPendingReply<ManagedObjectList> reply = managerBluez5->GetManagedObjects(); + reply.waitForFinished(); + if (!reply.isError()) { + foreach (const QDBusObjectPath &path, reply.value().keys()) { + const InterfaceList ifaceList = reply.value().value(path); + foreach (const QString &iface, ifaceList.keys()) { + if (iface == QStringLiteral("org.bluez.Device1")) { + + if (path.path().indexOf(adapterBluez5->path()) != 0) + continue; //devices whose path doesn't start with same path we skip - deviceFoundBluez5(path); + deviceFoundBluez5(path.path()); + } + } + } } // wait 20s and sum up what was found diff --git a/src/bluetooth/qbluetoothlocaldevice.cpp b/src/bluetooth/qbluetoothlocaldevice.cpp index c5b68645..78899d0a 100644 --- a/src/bluetooth/qbluetoothlocaldevice.cpp +++ b/src/bluetooth/qbluetoothlocaldevice.cpp @@ -124,8 +124,16 @@ QBluetoothLocalDevice::~QBluetoothLocalDevice() } /*! - Returns true if the QBluetoothLocalDevice represents an available local Bluetooth device; + Returns \c true if the QBluetoothLocalDevice represents an available local Bluetooth device; otherwise return false. + + If the local Bluetooth adapter represented by an instance of this class + is removed from the system (e.g. removal of the underlying Bluetooth dongle) + then this instance will become invalid. An already invalid QBluetoothLocalDevice instance + remains invalid even if the same Bluetooth adapter is returned to + the system. + + \sa allDevices() */ bool QBluetoothLocalDevice::isValid() const { diff --git a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp index 98894dff..18493f82 100644 --- a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp @@ -286,7 +286,7 @@ static inline OrgBluezDeviceInterface *getDevice(const QBluetoothAddress &addres void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) { - if (address.isNull() || !isValid()) { + if (!isValid() || address.isNull()) { QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, Q_ARG(QBluetoothLocalDevice::Error, QBluetoothLocalDevice::PairingError)); @@ -636,6 +636,7 @@ QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice adapterProperties(0), managerBluez5(0), agent(0), + manager(0), localAddress(address), pairingTarget(0), pairingDiscoveryTimer(0), @@ -700,6 +701,8 @@ QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() delete managerBluez5; delete agent; delete pairingTarget; + delete manager; + qDeleteAll(devices); qDeleteAll(deviceChangeMonitors); } @@ -709,11 +712,12 @@ void QBluetoothLocalDevicePrivate::initializeAdapter() if (adapter) return; - OrgBluezManagerInterface manager(QStringLiteral("org.bluez"), QStringLiteral("/"), - QDBusConnection::systemBus()); + QScopedPointer<OrgBluezManagerInterface> man(new OrgBluezManagerInterface( + QStringLiteral("org.bluez"), QStringLiteral("/"), + QDBusConnection::systemBus())); if (localAddress == QBluetoothAddress()) { - QDBusPendingReply<QDBusObjectPath> reply = manager.DefaultAdapter(); + QDBusPendingReply<QDBusObjectPath> reply = man->DefaultAdapter(); reply.waitForFinished(); if (reply.isError()) return; @@ -721,7 +725,7 @@ void QBluetoothLocalDevicePrivate::initializeAdapter() adapter = new OrgBluezAdapterInterface(QStringLiteral("org.bluez"), reply.value().path(), QDBusConnection::systemBus()); } else { - QDBusPendingReply<QList<QDBusObjectPath> > reply = manager.ListAdapters(); + QDBusPendingReply<QList<QDBusObjectPath> > reply = man->ListAdapters(); reply.waitForFinished(); if (reply.isError()) return; @@ -733,8 +737,10 @@ void QBluetoothLocalDevicePrivate::initializeAdapter() QDBusPendingReply<QVariantMap> reply = tmpAdapter->GetProperties(); reply.waitForFinished(); - if (reply.isError()) + if (reply.isError()) { + delete tmpAdapter; continue; + } QBluetoothAddress path_address(reply.value().value(QStringLiteral("Address")).toString()); @@ -747,6 +753,11 @@ void QBluetoothLocalDevicePrivate::initializeAdapter() } } + // monitor case when local adapter is removed + manager = man.take(); + connect(manager, SIGNAL(AdapterRemoved(QDBusObjectPath)), + this, SLOT(adapterRemoved(QDBusObjectPath))); + currentMode = static_cast<QBluetoothLocalDevice::HostMode>(-1); if (adapter) { connect(adapter, SIGNAL(PropertyChanged(QString, QDBusVariant)), @@ -775,51 +786,14 @@ void QBluetoothLocalDevicePrivate::initializeAdapterBluez5() connect(managerBluez5, SIGNAL(InterfacesRemoved(QDBusObjectPath,QStringList)), SLOT(InterfacesRemoved(QDBusObjectPath,QStringList))); - QDBusPendingReply<ManagedObjectList> reply = managerBluez5->GetManagedObjects(); - reply.waitForFinished(); - if (reply.isError()) + bool ok = true; + const QString adapterPath = findAdapterForAddress(localAddress, &ok); + if (!ok || adapterPath.isEmpty()) return; - typedef QPair<QString, QBluetoothAddress> AddressForPathType; - QList<AddressForPathType> localAdapters; - - foreach (const QDBusObjectPath &path, reply.value().keys()) { - const InterfaceList ifaceList = reply.value().value(path); - foreach (const QString &iface, ifaceList.keys()) { - if (iface == QStringLiteral("org.bluez.Adapter1")) { - AddressForPathType pair; - pair.first = path.path(); - pair.second = QBluetoothAddress(ifaceList.value(iface).value( - QStringLiteral("Address")).toString()); - if (!pair.second.isNull()) - localAdapters.append(pair); - break; - } - } - } - - if (localAdapters.isEmpty()) - return; - - if (localAddress.isNull()) { - //concept of DefaultAdapter doesn't exist anymore. - //we define the first adapter as default - adapterBluez5 = new OrgBluezAdapter1Interface(QStringLiteral("org.bluez"), - localAdapters.front().first, - QDBusConnection::systemBus(), this); - } else { - foreach (const AddressForPathType &pair, localAdapters) { - if (pair.second == localAddress) { - adapterBluez5 = new OrgBluezAdapter1Interface(QStringLiteral("org.bluez"), - pair.first, - QDBusConnection::systemBus(), this); - break; - } - } - - if (!adapterBluez5) //no match - return; - } + adapterBluez5 = new OrgBluezAdapter1Interface(QStringLiteral("org.bluez"), + adapterPath, + QDBusConnection::systemBus(), this); if (adapterBluez5) { //hook up propertiesChanged for current adapter @@ -927,10 +901,11 @@ void QBluetoothLocalDevicePrivate::InterfacesAdded(const QDBusObjectPath &object } } -void QBluetoothLocalDevicePrivate::InterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces) +void QBluetoothLocalDevicePrivate::InterfacesRemoved(const QDBusObjectPath &object_path, + const QStringList &interfaces) { if (deviceChangeMonitors.contains(object_path.path()) - && interfaces.contains(QStringLiteral("org.bluez.Device1"))) { + && interfaces.contains(QLatin1String("org.bluez.Device1"))) { //a device was removed delete deviceChangeMonitors.take(object_path.path()); @@ -944,6 +919,27 @@ void QBluetoothLocalDevicePrivate::InterfacesRemoved(const QDBusObjectPath &obje if (found) emit q_ptr->deviceDisconnected(address); } + + if (adapterBluez5 + && object_path.path() == adapterBluez5->path() + && interfaces.contains(QLatin1String("org.bluez.Adapter1"))) { + qCDebug(QT_BT_BLUEZ) << "Adapter" << adapterBluez5->path() << "was removed"; + // current adapter was removed -> invalidate the instance + delete adapterBluez5; + adapterBluez5 = 0; + managerBluez5->deleteLater(); + managerBluez5 = 0; + delete adapterProperties; + adapterProperties = 0; + + delete pairingTarget; + pairingTarget = 0; + + // turn off connectivity monitoring + qDeleteAll(deviceChangeMonitors); + deviceChangeMonitors.clear(); + connectedDevicesSet.clear(); + } } bool QBluetoothLocalDevicePrivate::isValid() const @@ -951,6 +947,39 @@ bool QBluetoothLocalDevicePrivate::isValid() const return adapter || adapterBluez5; } +// Bluez 4 +void QBluetoothLocalDevicePrivate::adapterRemoved(const QDBusObjectPath &devicePath) +{ + if (!adapter ) + return; + + if (adapter->path() != devicePath.path()) + return; + + qCDebug(QT_BT_BLUEZ) << "Adapter" << devicePath.path() + << "was removed. Invalidating object."; + // the current adapter was removed + delete adapter; + adapter = 0; + manager->deleteLater(); + manager = 0; + + // stop all pairing related activities + if (agent) { + QDBusConnection::systemBus().unregisterObject(agent_path); + delete agent; + agent = 0; + } + + delete msgConnection; + msgConnection = 0; + + // stop all connectivity monitoring + qDeleteAll(devices); + devices.clear(); + connectedDevicesSet.clear(); +} + void QBluetoothLocalDevicePrivate::RequestConfirmation(const QDBusObjectPath &in0, uint in1) { Q_UNUSED(in0); diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index 8edac58c..55086012 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -61,6 +61,7 @@ class OrgFreedesktopDBusObjectManagerInterface; class OrgBluezAgentAdaptor; class OrgBluezDeviceInterface; class OrgBluezDevice1Interface; +class OrgBluezManagerInterface; QT_BEGIN_NAMESPACE class QDBusPendingCallWatcher; @@ -137,6 +138,7 @@ public: OrgFreedesktopDBusObjectManagerInterface *managerBluez5; //Bluez 5 QMap<QString, OrgFreedesktopDBusPropertiesInterface *> deviceChangeMonitors; //Bluez 5 OrgBluezAgentAdaptor *agent; + OrgBluezManagerInterface *manager; QList<QBluetoothAddress> connectedDevices() const; @@ -167,6 +169,7 @@ public slots: void _q_deviceRemoved(const QDBusObjectPath &device); void _q_devicePropertyChanged(const QString &property, const QDBusVariant &value); bool isValid() const; + void adapterRemoved(const QDBusObjectPath &device); void requestPairingBluez5(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing targetPairing); diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp index 7d2f1b1c..8e5c3aaa 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp @@ -155,38 +155,17 @@ void QBluetoothServiceDiscoveryAgentPrivate::startBluez5(const QBluetoothAddress if (foundHostAdapterPath.isEmpty()) { // check that we match adapter addresses or use first if it wasn't specified - QDBusPendingReply<ManagedObjectList> reply = managerBluez5->GetManagedObjects(); - reply.waitForFinished(); - if (reply.isError()) { + bool ok = false; + foundHostAdapterPath = findAdapterForAddress(m_deviceAdapterAddress, &ok); + if (!ok) { discoveredDevices.clear(); error = QBluetoothServiceDiscoveryAgent::InputOutputError; - errorString = reply.error().message(); + errorString = QBluetoothDeviceDiscoveryAgent::tr("Cannot access adapter during service discovery"); emit q->error(error); _q_serviceDiscoveryFinished(); return; } - const QString desiredAdapter = m_deviceAdapterAddress.toString(); - foreach (const QDBusObjectPath &path, reply.value().keys()) { - const InterfaceList ifaceList = reply.value().value(path); - foreach (const QString &iface, ifaceList.keys()) { - if (iface == QStringLiteral("org.bluez.Adapter1")) { - if (m_deviceAdapterAddress.isNull() - || desiredAdapter == ifaceList.value(iface). - value(QStringLiteral("Address")).toString()) { - // use first adapter or we just matched one - foundHostAdapterPath = path.path(); - } - - if (!foundHostAdapterPath.isEmpty()) - break; - } - } - - if (!foundHostAdapterPath.isEmpty()) - break; - } - if (foundHostAdapterPath.isEmpty()) { // Cannot find a local adapter // Abort any outstanding discoveries @@ -551,7 +530,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC foreach (const QString &record, reply.value()) { bool isBtleService = false; - const QBluetoothServiceInfo serviceInfo = parseServiceXml(record, &isBtleService); + QBluetoothServiceInfo serviceInfo = parseServiceXml(record, &isBtleService); if (isBtleService) { qCDebug(QT_BT_BLUEZ) << "Discovered BLE services" << discoveredDevices.at(0).address().toString() @@ -566,6 +545,20 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC // search pattern during DiscoverServices() call Q_Q(QBluetoothServiceDiscoveryAgent); + // Some service uuids are unknown to Bluez. In such cases we fall back + // to our own naming resolution. + if (serviceInfo.serviceName().isEmpty() + && !serviceInfo.serviceClassUuids().isEmpty()) { + foreach (const QBluetoothUuid &classUuid, serviceInfo.serviceClassUuids()) { + bool ok = false; + QBluetoothUuid::ServiceClassUuid clsId + = static_cast<QBluetoothUuid::ServiceClassUuid>(classUuid.toUInt16(&ok)); + if (ok) { + serviceInfo.setServiceName(QBluetoothUuid::serviceClassToString(clsId)); + break; + } + } + } if (!isDuplicatedService(serviceInfo)) { discoveredServices.append(serviceInfo); @@ -878,7 +871,7 @@ QVariant QBluetoothServiceDiscoveryAgentPrivate::readAttributeValue(QXmlStreamRe } xml.skipCurrentElement(); return QVariant::fromValue(uuid); - } else if (xml.name() == QLatin1String("text")) { + } else if (xml.name() == QLatin1String("text") || xml.name() == QLatin1String("url")) { QString value = xml.attributes().value(QStringLiteral("value")).toString(); if (xml.attributes().value(QStringLiteral("encoding")) == QLatin1String("hex")) value = QString::fromUtf8(QByteArray::fromHex(value.toLatin1())); diff --git a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp index 93fe22bf..eb764047 100644 --- a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp @@ -44,14 +44,20 @@ #include "bluez/manager_p.h" #include "bluez/service_p.h" +#include "bluez/bluez5_helper_p.h" +#include "bluez/profile1_p.h" #include <QtCore/QLoggingCategory> #include <QtCore/QXmlStreamWriter> +#include <QtCore/QAtomicInt> QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) +static const QLatin1String profilePathTemplate("/qt/profile"); +static QAtomicInt pathCounter; + static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute) { const QString unsignedFormat(QStringLiteral("0x%1")); @@ -171,8 +177,14 @@ static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute) } QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate() -: service(0), serviceRecord(0), registered(false) +: service(0), serviceBluez5(0), serviceRecord(0), registered(false) { + if (isBluez5()) { + serviceBluez5 = new OrgBluezProfileManager1Interface( + QStringLiteral("org.bluez"), + QStringLiteral("/org/bluez"), + QDBusConnection::systemBus(), this); + } } QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate() @@ -189,15 +201,30 @@ bool QBluetoothServiceInfoPrivate::unregisterService() if (!registered) return false; - if (!ensureSdpConnection(currentLocalAdapter)) - return false; + if (serviceBluez5) { // Bluez 5 + if (profilePath.isEmpty()) + return false; - QDBusPendingReply<> reply = service->RemoveRecord(serviceRecord); - reply.waitForFinished(); - if (reply.isError()) - return false; + QDBusPendingReply<> reply = serviceBluez5->UnregisterProfile( + QDBusObjectPath(profilePath)); + reply.waitForFinished(); + if (reply.isError()) { + qCWarning(QT_BT_BLUEZ) << "Cannot unregister profile:" + << profilePath << reply.error().message(); + return false; + } + profilePath.clear(); + } else { // Bluez 4 + if (!ensureSdpConnection(currentLocalAdapter)) + return false; - serviceRecord = 0; + QDBusPendingReply<> reply = service->RemoveRecord(serviceRecord); + reply.waitForFinished(); + if (reply.isError()) + return false; + + serviceRecord = 0; + } registered = false; return true; @@ -232,13 +259,18 @@ bool QBluetoothServiceInfoPrivate::ensureSdpConnection(const QBluetoothAddress & bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter) { - //if new adapter unregister previous one first - if (registered && localAdapter != currentLocalAdapter) - unregisterService(); + if (serviceBluez5) { // Bluez 5 + if (registered) + return false; + } else { // Bluez 4 + //if new adapter unregister previous one first + if (registered && localAdapter != currentLocalAdapter) + unregisterService(); - if (!ensureSdpConnection(localAdapter)) { - qCWarning(QT_BT_BLUEZ) << "SDP not connected. Cannot register"; - return false; + if (!ensureSdpConnection(localAdapter)) { + qCWarning(QT_BT_BLUEZ) << "SDP not connected. Cannot register"; + return false; + } } QString xmlServiceRecord; @@ -266,22 +298,84 @@ bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &loca stream.writeEndDocument(); - if (!registered) { - QDBusPendingReply<uint> reply = service->AddRecord(xmlServiceRecord); - reply.waitForFinished(); - if (reply.isError()) { - qCWarning(QT_BT_BLUEZ) << "AddRecord returned error" << reply.error(); - return false; + if (serviceBluez5) { // Bluez 5 + // create path + profilePath = profilePathTemplate; + profilePath.append(QString::fromLatin1("/%1%2/%3"). + arg(QCoreApplication::applicationName()). + arg(QCoreApplication::applicationPid()). + arg(pathCounter.fetchAndAddOrdered(1))); + + QVariantMap mapping; + mapping.insert(QStringLiteral("ServiceRecord"), xmlServiceRecord); + + // Strategy to pick service uuid + // 1.) use serviceUuid() + // 2.) use first custom uuid if available + // 3.) use first service class uuid + QBluetoothUuid profileUuid = attributes.value(QBluetoothServiceInfo::ServiceId) + .value<QBluetoothUuid>(); + QBluetoothUuid firstCustomUuid; + if (profileUuid.isNull()) { + const QVariant var = attributes.value(QBluetoothServiceInfo::ServiceClassIds); + if (var.isValid()) { + const QBluetoothServiceInfo::Sequence seq + = var.value<QBluetoothServiceInfo::Sequence>(); + QBluetoothUuid tempUuid; + + for (int i = 0; i < seq.count(); i++) { + tempUuid = seq.at(i).value<QBluetoothUuid>(); + if (tempUuid.isNull()) + continue; + + int size = tempUuid.minimumSize(); + if (size == 2 || size == 4) { // Base UUID derived + if (profileUuid.isNull()) + profileUuid = tempUuid; + } else if (firstCustomUuid.isNull()){ + firstCustomUuid = tempUuid; + } + } + } } - serviceRecord = reply.value(); - } else { - QDBusPendingReply<> reply = service->UpdateRecord(serviceRecord, xmlServiceRecord); + if (!firstCustomUuid.isNull()) + profileUuid = firstCustomUuid; + + QString uuidString = profileUuid.toString(); + uuidString.chop(1); // remove trailing '}' + uuidString.remove(0, 1); // remove beginning '{' + + qCDebug(QT_BT_BLUEZ) << "Registering profile under" << profilePath + << uuidString; + + QDBusPendingReply<> reply = serviceBluez5->RegisterProfile( + QDBusObjectPath(profilePath), + uuidString, + mapping); reply.waitForFinished(); if (reply.isError()) { - qCWarning(QT_BT_BLUEZ) << "UpdateRecord returned error" << reply.error(); + qCWarning(QT_BT_BLUEZ) << "Cannot register profile" << reply.error().message(); return false; } + } else { // Bluez 4 + if (!registered) { + QDBusPendingReply<uint> reply = service->AddRecord(xmlServiceRecord); + reply.waitForFinished(); + if (reply.isError()) { + qCWarning(QT_BT_BLUEZ) << "AddRecord returned error" << reply.error(); + return false; + } + + serviceRecord = reply.value(); + } else { + QDBusPendingReply<> reply = service->UpdateRecord(serviceRecord, xmlServiceRecord); + reply.waitForFinished(); + if (reply.isError()) { + qCWarning(QT_BT_BLUEZ) << "UpdateRecord returned error" << reply.error(); + return false; + } + } } registered = true; diff --git a/src/bluetooth/qbluetoothserviceinfo_p.h b/src/bluetooth/qbluetoothserviceinfo_p.h index 46ae84d4..3dc32b5c 100644 --- a/src/bluetooth/qbluetoothserviceinfo_p.h +++ b/src/bluetooth/qbluetoothserviceinfo_p.h @@ -51,6 +51,7 @@ #include <QVariant> class OrgBluezServiceInterface; +class OrgBluezProfileManager1Interface; QT_BEGIN_NAMESPACE @@ -80,8 +81,10 @@ private: bool ensureSdpConnection(const QBluetoothAddress &localAdapter = QBluetoothAddress()); OrgBluezServiceInterface *service; + OrgBluezProfileManager1Interface *serviceBluez5; quint32 serviceRecord; QBluetoothAddress currentLocalAdapter; + QString profilePath; #endif mutable bool registered; diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h index c6a8bd72..f0c45c0e 100644 --- a/src/bluetooth/qbluetoothsocket.h +++ b/src/bluetooth/qbluetoothsocket.h @@ -66,6 +66,7 @@ class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice public: + // TODO Decouple SocketState and SocketError enum values from QAbstractSocket in Qt 6 enum SocketState { UnconnectedState = QAbstractSocket::UnconnectedState, ServiceLookupState = QAbstractSocket::HostLookupState, diff --git a/src/bluetooth/qbluetoothsocket_bluez.cpp b/src/bluetooth/qbluetoothsocket_bluez.cpp index c4d84211..ba99b889 100644 --- a/src/bluetooth/qbluetoothsocket_bluez.cpp +++ b/src/bluetooth/qbluetoothsocket_bluez.cpp @@ -139,7 +139,7 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, int result = -1; if (socket == -1 && !ensureNativeSocket(socketType)) { - errorString = QObject::tr("Unknown socket error"); + errorString = QBluetoothSocket::tr("Unknown socket error"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return; } diff --git a/src/bluetooth/qbluetoothtransferreply.cpp b/src/bluetooth/qbluetoothtransferreply.cpp index 9acd917d..4dc1d4c8 100644 --- a/src/bluetooth/qbluetoothtransferreply.cpp +++ b/src/bluetooth/qbluetoothtransferreply.cpp @@ -63,13 +63,14 @@ QT_BEGIN_NAMESPACE This enum describes the type of error that occurred - \value NoError No error. - \value UnknownError Unknown error, no better enum available - \value FileNotFoundError Unable to open the file specified - \value HostNotFoundError Unable to connect to the target host - \value UserCanceledTransferError User terminated the transfer - \value IODeviceNotReadableError File was not open before initiating the sending command - \value ResourceBusyError Unable to access the resource. + \value NoError No error. + \value UnknownError Unknown error, no better enum available. + \value FileNotFoundError Unable to open the file specified. + \value HostNotFoundError Unable to connect to the target host. + \value UserCanceledTransferError User terminated the transfer. + \value IODeviceNotReadableError File was not open before initiating the sending command. + \value ResourceBusyError Unable to access the resource.. + \value SessionError An error occurred during the handling of the session. */ diff --git a/src/bluetooth/qbluetoothtransferreply.h b/src/bluetooth/qbluetoothtransferreply.h index 33910fd9..ccf2d71d 100644 --- a/src/bluetooth/qbluetoothtransferreply.h +++ b/src/bluetooth/qbluetoothtransferreply.h @@ -63,7 +63,8 @@ public: HostNotFoundError, UserCanceledTransferError, IODeviceNotReadableError, - ResourceBusyError + ResourceBusyError, + SessionError }; @@ -83,6 +84,7 @@ public Q_SLOTS: void abort(); Q_SIGNALS: + //TODO Remove QBluetoothTransferReply* parameter in Qt 6 void finished(QBluetoothTransferReply *); void transferProgress(qint64 bytesTransferred, qint64 bytesTotal); diff --git a/src/bluetooth/qbluetoothtransferreply_bluez.cpp b/src/bluetooth/qbluetoothtransferreply_bluez.cpp index e4395e2f..4f5eefeb 100644 --- a/src/bluetooth/qbluetoothtransferreply_bluez.cpp +++ b/src/bluetooth/qbluetoothtransferreply_bluez.cpp @@ -44,17 +44,23 @@ #include "qbluetoothaddress.h" #include "bluez/obex_client_p.h" -#include "bluez/obex_manager_p.h" #include "bluez/obex_agent_p.h" #include "bluez/obex_transfer_p.h" +#include "bluez/bluez5_helper_p.h" +#include "bluez/obex_client1_bluez5_p.h" +#include "bluez/obex_objectpush1_bluez5_p.h" +#include "bluez/obex_transfer1_bluez5_p.h" +#include "bluez/properties_p.h" #include "qbluetoothtransferreply.h" +#include <QtCore/QAtomicInt> #include <QtCore/QLoggingCategory> #include <QFuture> #include <QFutureWatcher> #include <QtConcurrentRun> static const QLatin1String agentPath("/qt/agent"); +static QAtomicInt agentPathCounter; QT_BEGIN_NAMESPACE @@ -62,26 +68,40 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) QBluetoothTransferReplyBluez::QBluetoothTransferReplyBluez(QIODevice *input, const QBluetoothTransferRequest &request, QBluetoothTransferManager *parent) -: QBluetoothTransferReply(parent), tempfile(0), source(input), +: QBluetoothTransferReply(parent), + m_client(0), m_agent(0), m_clientBluez(0), m_objectPushBluez(0), + m_tempfile(0), m_source(input), m_running(false), m_finished(false), m_size(0), m_error(QBluetoothTransferReply::NoError), m_errorStr(), m_transfer_path() { setRequest(request); setManager(parent); - client = new OrgOpenobexClientInterface(QStringLiteral("org.openobex.client"), QStringLiteral("/"), - QDBusConnection::sessionBus()); - qsrand(QTime::currentTime().msec()); - m_agent_path = agentPath; - m_agent_path.append(QString::fromLatin1("/%1").arg(qrand())); + qRegisterMetaType<QBluetoothTransferReply*>("QBluetoothTransferReply*"); - agent = new AgentAdaptor(this); + if (isBluez5()) { + m_clientBluez = new OrgBluezObexClient1Interface(QStringLiteral("org.bluez.obex"), + QStringLiteral("/org/bluez/obex"), + QDBusConnection::sessionBus(), this); - bool res = QDBusConnection::sessionBus().registerObject(m_agent_path, this); - if(!res) - qCWarning(QT_BT_BLUEZ) << "Failed Creating dbus objects"; - qRegisterMetaType<QBluetoothTransferReply*>("QBluetoothTransferReply*"); + } else { + m_client = new OrgOpenobexClientInterface(QStringLiteral("org.openobex.client"), + QStringLiteral("/"), + QDBusConnection::sessionBus()); + + m_agent_path = agentPath; + m_agent_path.append(QStringLiteral("/%1%2/%3"). + arg(QCoreApplication::applicationName()). + arg(QCoreApplication::applicationPid()). + arg(agentPathCounter.fetchAndAddOrdered(1))); + + m_agent = new AgentAdaptor(this); + + if (!QDBusConnection::sessionBus().registerObject(m_agent_path, this)) + qCWarning(QT_BT_BLUEZ) << "Failed creating obex agent dbus objects"; + } + QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection); m_running = true; } @@ -92,19 +112,18 @@ QBluetoothTransferReplyBluez::QBluetoothTransferReplyBluez(QIODevice *input, con QBluetoothTransferReplyBluez::~QBluetoothTransferReplyBluez() { QDBusConnection::sessionBus().unregisterObject(m_agent_path); - delete client; + delete m_client; } bool QBluetoothTransferReplyBluez::start() { -// qDebug() << "Got a:" << source->metaObject()->className(); - QFile *file = qobject_cast<QFile *>(source); + QFile *file = qobject_cast<QFile *>(m_source); if(!file){ - tempfile = new QTemporaryFile(this ); - tempfile->open(); - qCDebug(QT_BT_BLUEZ) << "Not a QFile, making a copy" << tempfile->fileName(); - if (!source->isReadable()) { + m_tempfile = new QTemporaryFile(this ); + m_tempfile->open(); + qCDebug(QT_BT_BLUEZ) << "Not a QFile, making a copy" << m_tempfile->fileName(); + if (!m_source->isReadable()) { m_errorStr = QBluetoothTransferReply::tr("QIODevice cannot be read." "Make sure it is open for reading."); m_error = QBluetoothTransferReply::IODeviceNotReadableError; @@ -117,7 +136,7 @@ bool QBluetoothTransferReplyBluez::start() QFutureWatcher<bool> *watcher = new QFutureWatcher<bool>(); QObject::connect(watcher, SIGNAL(finished()), this, SLOT(copyDone())); - QFuture<bool> results = QtConcurrent::run(QBluetoothTransferReplyBluez::copyToTempFile, tempfile, source); + QFuture<bool> results = QtConcurrent::run(QBluetoothTransferReplyBluez::copyToTempFile, m_tempfile, m_source); watcher->setFuture(results); } else { @@ -158,27 +177,154 @@ bool QBluetoothTransferReplyBluez::copyToTempFile(QIODevice *to, QIODevice *from return true; } +void QBluetoothTransferReplyBluez::cleanupSession() +{ + if (!m_objectPushBluez) + return; + + QDBusPendingReply<> reply = m_clientBluez->RemoveSession(QDBusObjectPath(m_objectPushBluez->path())); + reply.waitForFinished(); + if (reply.isError()) + qCWarning(QT_BT_BLUEZ) << "Abort: Cannot remove obex session"; + + delete m_objectPushBluez; + m_objectPushBluez = 0; +} + void QBluetoothTransferReplyBluez::copyDone() { - m_size = tempfile->size(); - startOPP(tempfile->fileName()); + m_size = m_tempfile->size(); + startOPP(m_tempfile->fileName()); QObject::sender()->deleteLater(); } -void QBluetoothTransferReplyBluez::startOPP(QString filename) +void QBluetoothTransferReplyBluez::sessionCreated(QDBusPendingCallWatcher *watcher) { - QVariantMap device; - QStringList files; + QDBusPendingReply<QDBusObjectPath> reply = *watcher; + if (reply.isError()) { + qCWarning(QT_BT_BLUEZ) << "Failed to create obex session:" + << reply.error().name() << reply.reply().errorMessage(); + + m_errorStr = QBluetoothTransferReply::tr("Invalid target address"); + m_error = QBluetoothTransferReply::HostNotFoundError; + m_finished = true; + m_running = false; + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection, + Q_ARG(QBluetoothTransferReply*, this)); + + watcher->deleteLater(); + return; + } + + m_objectPushBluez = new OrgBluezObexObjectPush1Interface(QStringLiteral("org.bluez.obex"), + reply.value().path(), + QDBusConnection::sessionBus(), this); + QDBusPendingReply<QDBusObjectPath, QVariantMap> newReply = m_objectPushBluez->SendFile(fileToTranser); + QDBusPendingCallWatcher *newWatcher = new QDBusPendingCallWatcher(newReply, this); + connect(newWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(sessionStarted(QDBusPendingCallWatcher*))); + watcher->deleteLater(); +} + +void QBluetoothTransferReplyBluez::sessionStarted(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply<QDBusObjectPath, QVariantMap> reply = *watcher; + if (reply.isError()) { + qCWarning(QT_BT_BLUEZ) << "Failed to start obex session:" + << reply.error().name() << reply.reply().errorMessage(); + + m_errorStr = QBluetoothTransferReply::tr("Push session cannot be started"); + m_error = QBluetoothTransferReply::SessionError; + m_finished = true; + m_running = false; + + cleanupSession(); + + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection, + Q_ARG(QBluetoothTransferReply *, this)); + + watcher->deleteLater(); + return; + } - device.insert(QString::fromLatin1("Destination"), request().address().toString()); - files << filename; + const QDBusObjectPath path = reply.argumentAt<0>(); + const QVariantMap map = reply.argumentAt<1>(); + m_transfer_path = path.path(); - QDBusObjectPath path(m_agent_path); - QDBusPendingReply<> sendReply = client->SendFiles(device, files, path); + //watch the transfer + OrgFreedesktopDBusPropertiesInterface *properties = new OrgFreedesktopDBusPropertiesInterface( + QStringLiteral("org.bluez.obex"), path.path(), + QDBusConnection::sessionBus(), this); + connect(properties, SIGNAL(PropertiesChanged(QString,QVariantMap,QStringList)), + SLOT(sessionChanged(QString,QVariantMap,QStringList))); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(sendReply, this); - QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(sendReturned(QDBusPendingCallWatcher*))); + watcher->deleteLater(); +} + +void QBluetoothTransferReplyBluez::sessionChanged(const QString &interface, + const QVariantMap &changed_properties, + const QStringList &) +{ + if (changed_properties.contains(QStringLiteral("Transferred"))) { + emit transferProgress( + changed_properties.value(QStringLiteral("Transferred")).toULongLong(), + m_size); + } + + if (changed_properties.contains(QStringLiteral("Status"))) { + const QString s = changed_properties. + value(QStringLiteral("Status")).toString(); + if (s == QStringLiteral("complete") + || s == QStringLiteral("error")) { + + m_transfer_path.clear(); + m_finished = true; + m_running = false; + + if (s == QStringLiteral("error")) { + m_error = QBluetoothTransferReply::UnknownError; + m_errorStr = tr("Unknown Error"); + } else { // complete + // allow progress bar to complete + emit transferProgress(m_size, m_size); + } + + cleanupSession(); + + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection, + Q_ARG(QBluetoothTransferReply*, this)); + } // ignore "active", "queued" & "suspended" status + } + qCDebug(QT_BT_BLUEZ) << "Transfer update:" << interface << changed_properties; +} + +void QBluetoothTransferReplyBluez::startOPP(const QString &filename) +{ + if (m_client) { // Bluez 4 + QVariantMap device; + QStringList files; + + device.insert(QStringLiteral("Destination"), request().address().toString()); + files << filename; + + QDBusObjectPath path(m_agent_path); + QDBusPendingReply<> sendReply = m_client->SendFiles(device, files, path); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(sendReply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(sendReturned(QDBusPendingCallWatcher*))); + } else { //Bluez 5 + fileToTranser = filename; + QVariantMap mapping; + mapping.insert(QStringLiteral("Target"), QStringLiteral("opp")); + + QDBusPendingReply<QDBusObjectPath> reply = m_clientBluez->CreateSession( + request().address().toString(), mapping); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(sessionCreated(QDBusPendingCallWatcher*))); + } } void QBluetoothTransferReplyBluez::sendReturned(QDBusPendingCallWatcher *watcher) @@ -278,16 +424,35 @@ bool QBluetoothTransferReplyBluez::isRunning() const void QBluetoothTransferReplyBluez::abort() { - if(!m_transfer_path.isEmpty()){ - OrgOpenobexTransferInterface *xfer = new OrgOpenobexTransferInterface(QStringLiteral("org.openobex.client"), - m_transfer_path, - QDBusConnection::sessionBus()); - QDBusPendingReply<> reply = xfer->Cancel(); + if (m_transfer_path.isEmpty()) + return; + + if (m_client) { + OrgOpenobexTransferInterface xfer(QStringLiteral("org.openobex.client"), + m_transfer_path, + QDBusConnection::sessionBus()); + + QDBusPendingReply<> reply = xfer.Cancel(); reply.waitForFinished(); - if(reply.isError()){ - qCWarning(QT_BT_BLUEZ) << "Failed to abort transfer" << reply.error(); - } - delete xfer; + if (reply.isError()) + qCWarning(QT_BT_BLUEZ) << "Failed to abort transfer" << reply.error().message(); + + } else if (m_clientBluez) { + OrgBluezObexTransfer1Interface iface(QStringLiteral("org.bluez.obex"), + m_transfer_path, + QDBusConnection::sessionBus()); + + QDBusPendingReply<> reply = iface.Cancel(); + reply.waitForFinished(); + if (reply.isError()) + qCDebug(QT_BT_BLUEZ) << "Failed to abort transfer" << reply.error().message(); + + m_error = QBluetoothTransferReply::UserCanceledTransferError; + m_errorStr = tr("Operation canceled"); + + cleanupSession(); + + emit finished(this); } } diff --git a/src/bluetooth/qbluetoothtransferreply_bluez_p.h b/src/bluetooth/qbluetoothtransferreply_bluez_p.h index 412959e5..657b550f 100644 --- a/src/bluetooth/qbluetoothtransferreply_bluez_p.h +++ b/src/bluetooth/qbluetoothtransferreply_bluez_p.h @@ -51,8 +51,9 @@ #include "qbluetoothtransferreply.h" class OrgOpenobexClientInterface; -class OrgOpenobexManagerInterface; class AgentAdaptor; +class OrgBluezObexClient1Interface; +class OrgBluezObexObjectPush1Interface; QT_BEGIN_NAMESPACE @@ -75,14 +76,16 @@ private slots: bool start(); private: - void startOPP(QString filename); + void startOPP(const QString &filename); - OrgOpenobexClientInterface *client; - OrgOpenobexManagerInterface *manager; - AgentAdaptor *agent; + OrgOpenobexClientInterface *m_client; + AgentAdaptor *m_agent; + OrgBluezObexClient1Interface *m_clientBluez; + OrgBluezObexObjectPush1Interface *m_objectPushBluez; - QTemporaryFile *tempfile; - QIODevice *source; + + QTemporaryFile *m_tempfile; + QIODevice *m_source; bool m_running; bool m_finished; @@ -95,11 +98,18 @@ private: QString m_agent_path; QString m_transfer_path; + QString fileToTranser; static bool copyToTempFile(QIODevice *to, QIODevice *from); + void cleanupSession(); private slots: void copyDone(); + void sessionCreated(QDBusPendingCallWatcher *watcher); + void sessionStarted(QDBusPendingCallWatcher *watcher); + void sessionChanged(const QString &interface, + const QVariantMap &changed_properties, + const QStringList &invalidated_properties); public slots: void abort(); |