diff options
-rw-r--r-- | src/bluetooth/bluetooth.pro | 2 | ||||
-rw-r--r-- | src/bluetooth/bluez/bluez.pri | 17 | ||||
-rw-r--r-- | src/bluetooth/bluez/bluez5_helper.cpp | 190 | ||||
-rw-r--r-- | src/bluetooth/bluez/bluez5_helper_p.h | 2 | ||||
-rw-r--r-- | src/bluetooth/bluez/bluez_data_p.h | 87 | ||||
-rw-r--r-- | src/bluetooth/bluez/objectmanager_p.h | 5 | ||||
-rw-r--r-- | src/bluetooth/doc/qtbluetooth.qdocconf | 1 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothserver.cpp | 2 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothserver_bluez.cpp | 5 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp | 99 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothsocket_android.cpp | 17 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothsocket_bluez.cpp | 5 | ||||
-rw-r--r-- | src/nfc/doc/qtnfc.qdocconf | 1 | ||||
-rw-r--r-- | src/src.pro | 4 | ||||
-rw-r--r-- | src/tools/sdpscanner/main.cpp | 332 | ||||
-rw-r--r-- | src/tools/sdpscanner/sdpscanner.pro | 24 | ||||
-rw-r--r-- | tests/bttestui/btlocaldevice.cpp | 26 | ||||
-rw-r--r-- | tests/bttestui/btlocaldevice.h | 1 | ||||
-rw-r--r-- | tests/bttestui/main.qml | 5 |
19 files changed, 533 insertions, 292 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 15b840e2..719d7ed5 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -100,8 +100,6 @@ config_bluez:qtHaveModule(dbus) { qlowenergycontroller_p.cpp } - CONFIG += link_pkgconfig - PKGCONFIG_PRIVATE += bluez } else:CONFIG(blackberry) { DEFINES += QT_QNX_BLUETOOTH diff --git a/src/bluetooth/bluez/bluez.pri b/src/bluetooth/bluez/bluez.pri index 851949d2..be5a02a7 100644 --- a/src/bluetooth/bluez/bluez.pri +++ b/src/bluetooth/bluez/bluez.pri @@ -1,16 +1,3 @@ -linux-*: { - # bluetooth.h is not standards compliant - contains(QMAKE_CXXFLAGS, -std=c++0x) { - QMAKE_CXXFLAGS -= -std=c++0x - QMAKE_CXXFLAGS += -std=gnu++0x - CONFIG -= c++11 - } - c++11 { - CONFIG -= c++11 - QMAKE_CXXFLAGS += -std=gnu++0x - } -} - HEADERS += bluez/manager_p.h \ bluez/adapter_p.h \ bluez/device_p.h \ @@ -29,8 +16,8 @@ HEADERS += bluez/manager_p.h \ bluez/profile1_p.h \ bluez/obex_client1_bluez5_p.h \ bluez/obex_objectpush1_bluez5_p.h \ - bluez/obex_transfer1_bluez5_p.h - + bluez/obex_transfer1_bluez5_p.h \ + bluez/bluez_data_p.h SOURCES += bluez/manager.cpp \ bluez/adapter.cpp \ diff --git a/src/bluetooth/bluez/bluez5_helper.cpp b/src/bluetooth/bluez/bluez5_helper.cpp index 18ab22b1..4e810d55 100644 --- a/src/bluetooth/bluez/bluez5_helper.cpp +++ b/src/bluetooth/bluez/bluez5_helper.cpp @@ -46,7 +46,6 @@ #include "objectmanager_p.h" #include "properties_p.h" #include "adapter1_bluez5_p.h" -#include <bluetooth/sdp_lib.h> QT_BEGIN_NAMESPACE @@ -270,194 +269,6 @@ void QtBluezDiscoveryManager::removeAdapterFromMonitoring(const QString &dbusPat emit discoveryInterrupted(dbusPath); } -#define BUFFER_SIZE 1024 - -static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray &xmlOutput) -{ - if (!data) - return; - - const int length = indentation*2 + 1; - QByteArray indentString(length, ' '); - - char snBuffer[BUFFER_SIZE]; - - xmlOutput.append(indentString); - - // deal with every dtd type - switch (data->dtd) { - case SDP_DATA_NIL: - xmlOutput.append("<nil/>\n"); - break; - case SDP_UINT8: - qsnprintf(snBuffer, BUFFER_SIZE, "<uint8 value=\"0x%02x\"/>\n", data->val.uint8); - xmlOutput.append(snBuffer); - break; - case SDP_UINT16: - qsnprintf(snBuffer, BUFFER_SIZE, "<uint16 value=\"0x%04x\"/>\n", data->val.uint16); - xmlOutput.append(snBuffer); - break; - case SDP_UINT32: - qsnprintf(snBuffer, BUFFER_SIZE, "<uint32 value=\"0x%08x\"/>\n", data->val.uint32); - xmlOutput.append(snBuffer); - break; - case SDP_UINT64: - qsnprintf(snBuffer, BUFFER_SIZE, "<uint64 value=\"0x%016x\"/>\n", data->val.uint64); - xmlOutput.append(snBuffer); - break; - case SDP_UINT128: - xmlOutput.append("<uint128 value=\"0x"); - for (int i = 0; i < 16; i++) - ::sprintf(&snBuffer[i * 2], "%02x", data->val.uint128.data[i]); - xmlOutput.append(snBuffer); - xmlOutput.append("\"/>\n"); - break; - case SDP_INT8: - qsnprintf(snBuffer, BUFFER_SIZE, "<int8 value=\"%d\"/>/n", data->val.int8); - xmlOutput.append(snBuffer); - break; - case SDP_INT16: - qsnprintf(snBuffer, BUFFER_SIZE, "<int16 value=\"%d\"/>/n", data->val.int16); - xmlOutput.append(snBuffer); - break; - case SDP_INT32: - qsnprintf(snBuffer, BUFFER_SIZE, "<int32 value=\"%d\"/>/n", data->val.int32); - xmlOutput.append(snBuffer); - break; - case SDP_INT64: - qsnprintf(snBuffer, BUFFER_SIZE, "<int64 value=\"%d\"/>/n", data->val.int64); - xmlOutput.append(snBuffer); - break; - case SDP_INT128: - xmlOutput.append("<int128 value=\"0x"); - for (int i = 0; i < 16; i++) - ::sprintf(&snBuffer[i * 2], "%02x", data->val.int128.data[i]); - xmlOutput.append(snBuffer); - xmlOutput.append("\"/>\n"); - break; - case SDP_UUID_UNSPEC: - break; - case SDP_UUID16: - case SDP_UUID32: - xmlOutput.append("<uuid value=\"0x"); - sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE); - xmlOutput.append(snBuffer); - xmlOutput.append("\"/>\n"); - break; - case SDP_UUID128: - xmlOutput.append("<uuid value=\""); - sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE); - xmlOutput.append(snBuffer); - xmlOutput.append("\"/>\n"); - break; - case SDP_TEXT_STR_UNSPEC: - break; - case SDP_TEXT_STR8: - case SDP_TEXT_STR16: - case SDP_TEXT_STR32: - { - xmlOutput.append("<text "); - QByteArray text = QByteArray::fromRawData(data->val.str, data->unitSize); - - bool hasNonPrintableChar = false; - for (int i = 0; i < text.count() && !hasNonPrintableChar; i++) { - if (!isprint(text[i])) { - hasNonPrintableChar = true; - break; - } - } - - if (hasNonPrintableChar) { - xmlOutput.append("encoding=\"hex\" value=\""); - xmlOutput.append(text.toHex()); - } else { - text.replace("&", "&"); - text.replace("<", "<"); - text.replace(">", ">"); - text.replace("\"", """); - - xmlOutput.append("value=\""); - xmlOutput.append(text); - } - - xmlOutput.append("\"/>\n"); - break; - } - case SDP_BOOL: - if (data->val.uint8) - xmlOutput.append("<boolean value=\"true\"/>\n"); - else - xmlOutput.append("<boolean value=\"false\"/>\n"); - break; - case SDP_SEQ_UNSPEC: - break; - case SDP_SEQ8: - case SDP_SEQ16: - case SDP_SEQ32: - xmlOutput.append("<sequence>\n"); - parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput); - xmlOutput.append(indentString); - xmlOutput.append("</sequence>\n"); - break; - case SDP_ALT_UNSPEC: - break; - case SDP_ALT8: - case SDP_ALT16: - case SDP_ALT32: - xmlOutput.append("<alternate>\n"); - parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput); - xmlOutput.append(indentString); - xmlOutput.append("</alternate>\n"); - break; - case SDP_URL_STR_UNSPEC: - break; - case SDP_URL_STR8: - case SDP_URL_STR16: - case SDP_URL_STR32: - strncpy(snBuffer, data->val.str, data->unitSize - 1); - xmlOutput.append("<url value=\""); - xmlOutput.append(snBuffer); - xmlOutput.append("\"/>\n"); - break; - default: - qDebug(QT_BT_BLUEZ) << "Unknown dtd type"; - } - - parseAttributeValues(data->next, indentation, xmlOutput); -} - -static void parseAttribute(void *value, void *extraData) -{ - sdp_data_t *data = (sdp_data_t *) value; - QByteArray *xmlOutput = static_cast<QByteArray *>(extraData); - - char buffer[BUFFER_SIZE]; - - ::qsnprintf(buffer, BUFFER_SIZE, " <attribute id=\"0x%04x\">\n", data->attrId); - xmlOutput->append(buffer); - - parseAttributeValues(data, 2, *xmlOutput); - - xmlOutput->append(" </attribute>\n"); -} - -// the resulting xml output is based on the already used xml parser -QByteArray parseSdpRecord(sdp_record_t *record) -{ - if (!record || !record->attrlist) - return QByteArray(); - - QByteArray xmlOutput; - - xmlOutput.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<record>\n"); - - sdp_list_foreach(record->attrlist, parseAttribute, &xmlOutput); - xmlOutput.append("</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. @@ -517,5 +328,4 @@ QString findAdapterForAddress(const QBluetoothAddress &wantedAddress, bool *ok = 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 36595f9c..4bec440e 100644 --- a/src/bluetooth/bluez/bluez5_helper_p.h +++ b/src/bluetooth/bluez/bluez5_helper_p.h @@ -44,7 +44,6 @@ #include <QtCore/QObject> #include <QtDBus/QtDBus> -#include <bluetooth/sdp.h> #include <QtBluetooth/QBluetoothAddress> typedef QMap<QString, QVariantMap> InterfaceList; @@ -57,7 +56,6 @@ QT_BEGIN_NAMESPACE bool isBluez5(); -QByteArray parseSdpRecord(sdp_record_t *record); QString findAdapterForAddress(const QBluetoothAddress &wantedAddress, bool *ok); class QtBluezDiscoveryManagerPrivate; diff --git a/src/bluetooth/bluez/bluez_data_p.h b/src/bluetooth/bluez/bluez_data_p.h new file mode 100644 index 00000000..822f53b9 --- /dev/null +++ b/src/bluetooth/bluez/bluez_data_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BLUEZ_DATA_P_H +#define BLUEZ_DATA_P_H + +#include <QtCore/qglobal.h> +#include <sys/socket.h> + +#define BTPROTO_L2CAP 0 +#define BTPROTO_RFCOMM 3 + +#define SOL_L2CAP 6 +#define SOL_RFCOMM 18 + +#define RFCOMM_LM 0x03 + +#define RFCOMM_LM_AUTH 0x0002 +#define RFCOMM_LM_ENCRYPT 0x0004 +#define RFCOMM_LM_TRUSTED 0x0008 +#define RFCOMM_LM_SECURE 0x0020 + +#define L2CAP_LM 0x03 +#define L2CAP_LM_AUTH 0x0002 +#define L2CAP_LM_ENCRYPT 0x0004 +#define L2CAP_LM_TRUSTED 0x0008 +#define L2CAP_LM_SECURE 0x0020 + +// Bluetooth address +typedef struct { + quint8 b[6]; +} __attribute__((packed)) bdaddr_t; + +// L2CP socket +struct sockaddr_l2 { + sa_family_t l2_family; + unsigned short l2_psm; + bdaddr_t l2_bdaddr; + unsigned short l2_cid; +}; + +// RFCOMM socket +struct sockaddr_rc { + sa_family_t rc_family; + bdaddr_t rc_bdaddr; + quint8 rc_channel; +}; + +#endif // BLUEZ_DATA_P_H diff --git a/src/bluetooth/bluez/objectmanager_p.h b/src/bluetooth/bluez/objectmanager_p.h index 6acf3d6d..5ecde487 100644 --- a/src/bluetooth/bluez/objectmanager_p.h +++ b/src/bluetooth/bluez/objectmanager_p.h @@ -19,6 +19,11 @@ #include <QtCore/QStringList> #include <QtCore/QVariant> #include <QtDBus/QtDBus> + +/* Temporary hack to merge branches until Bluez headers are removed + * from bluez5_helpers_p.h. + */ +#define NO_BLUEZ_INCLUDES #include "bluez5_helper_p.h" /* diff --git a/src/bluetooth/doc/qtbluetooth.qdocconf b/src/bluetooth/doc/qtbluetooth.qdocconf index 171310cb..52061d7e 100644 --- a/src/bluetooth/doc/qtbluetooth.qdocconf +++ b/src/bluetooth/doc/qtbluetooth.qdocconf @@ -2,7 +2,6 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtBluetooth description = Qt Bluetooth Reference Documentation -url = http://qt-project.org/doc/qt-$QT_VER version = $QT_VERSION examplesinstallpath = bluetooth diff --git a/src/bluetooth/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp index 8665e33e..3123b032 100644 --- a/src/bluetooth/qbluetoothserver.cpp +++ b/src/bluetooth/qbluetoothserver.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Copyright (C) 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** diff --git a/src/bluetooth/qbluetoothserver_bluez.cpp b/src/bluetooth/qbluetoothserver_bluez.cpp index e5967f7a..4532e570 100644 --- a/src/bluetooth/qbluetoothserver_bluez.cpp +++ b/src/bluetooth/qbluetoothserver_bluez.cpp @@ -43,14 +43,11 @@ #include "qbluetoothserver_p.h" #include "qbluetoothsocket.h" #include "qbluetoothlocaldevice.h" +#include "bluez/bluez_data_p.h" #include <QtCore/QLoggingCategory> #include <QtCore/QSocketNotifier> -#include <bluetooth/bluetooth.h> -#include <bluetooth/rfcomm.h> -#include <bluetooth/l2cap.h> - #include <errno.h> QT_BEGIN_NAMESPACE diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp index 1d7a6327..2f2f26d8 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp @@ -50,11 +50,10 @@ #include "bluez/objectmanager_p.h" #include "bluez/adapter1_bluez5_p.h" -#include <bluetooth/bluetooth.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> - +#include <QtCore/QFile> +#include <QtCore/QLibraryInfo> #include <QtCore/QLoggingCategory> +#include <QtCore/QProcess> #include <QtDBus/QDBusPendingCallWatcher> #include <QtConcurrent/QtConcurrentRun> @@ -205,76 +204,53 @@ void QBluetoothServiceDiscoveryAgentPrivate::startBluez5(const QBluetoothAddress /* * This function runs in a different thread. We need to be very careful what we * access from here. That's why invokeMethod is used below. + * + * src/tools/sdpscanner performs an SDP scan. This is + * done out-of-process to avoid license issues. At this stage Bluez uses GPLv2. */ void QBluetoothServiceDiscoveryAgentPrivate::runSdpScan( const QBluetoothAddress &remoteAddress, const QBluetoothAddress localAddress) { Q_Q(QBluetoothServiceDiscoveryAgent); - // connect to SDP server - bdaddr_t local, remote; - convertAddress(localAddress.toUInt64(), local.b); - convertAddress(remoteAddress.toUInt64(), remote.b); - - /* We use singleshot timer below because this function runs in a different - * thread than the rest of this class. - */ - - sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY); - // try one more time if first attempt fails - if (!session) - session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY); + const QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath); - qCDebug(QT_BT_BLUEZ) << "SDP for" << remoteAddress.toString() << session << qt_error_string(errno); - if (!session) { - if (singleDevice) { - // was sole device without result -> error - QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, + QFileInfo fileInfo(binPath, QStringLiteral("sdpscanner")); + if (!fileInfo.exists() || !fileInfo.isExecutable()) { + QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, Q_ARG(QBluetoothServiceDiscoveryAgent::Error, QBluetoothServiceDiscoveryAgent::InputOutputError), Q_ARG(QString, - QBluetoothServiceDiscoveryAgent::tr("Unable to access device")), + QBluetoothServiceDiscoveryAgent::tr("Unable to find sdpscanner")), Q_ARG(QStringList, QStringList())); - } else { - // go to next device - QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, - Q_ARG(QBluetoothServiceDiscoveryAgent::Error, - QBluetoothServiceDiscoveryAgent::NoError), - Q_ARG(QString, QString()), - Q_ARG(QStringList, QStringList())); - } - + qCWarning(QT_BT_BLUEZ) << "Cannot find sdpscanner:" + << fileInfo.canonicalFilePath(); return; } + QStringList arguments; + arguments << remoteAddress.toString() << localAddress.toString(); - // set the filter for service matches - uuid_t publicBrowseGroupUuid; - sdp_uuid16_create(&publicBrowseGroupUuid, QBluetoothUuid::PublicBrowseGroup); - sdp_list_t *serviceFilter; - serviceFilter = sdp_list_append(0, &publicBrowseGroupUuid); - - uint32_t attributeRange = 0x0000ffff; //all attributes - sdp_list_t *attributes; - attributes = sdp_list_append(0, &attributeRange); + QProcess process; + process.setProcessChannelMode(QProcess::ForwardedErrorChannel); + process.setReadChannel(QProcess::StandardOutput); + process.start(fileInfo.canonicalFilePath(), arguments); + process.waitForFinished(); - sdp_list_t* sdpResults; - int result = sdp_service_search_attr_req(session, serviceFilter, SDP_ATTR_REQ_RANGE, - attributes, &sdpResults); - sdp_list_free(attributes, 0); - sdp_list_free(serviceFilter, 0); - - if (result != 0) { - qCDebug(QT_BT_BLUEZ) << "SDP search failed" << qt_error_string(errno); - sdp_close(session); + if (process.exitStatus() != QProcess::NormalExit + || process.exitCode() != 0) { + qCWarning(QT_BT_BLUEZ) << "SDP scan failure" + << process.exitStatus() << process.exitCode() + << remoteAddress; if (singleDevice) { QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, Q_ARG(QBluetoothServiceDiscoveryAgent::Error, QBluetoothServiceDiscoveryAgent::InputOutputError), Q_ARG(QString, - QBluetoothServiceDiscoveryAgent::tr("Unable to access device")), + QBluetoothServiceDiscoveryAgent::tr("Unable to perform SDP scan")), Q_ARG(QStringList, QStringList())); } else { + // go to next device QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, Q_ARG(QBluetoothServiceDiscoveryAgent::Error, QBluetoothServiceDiscoveryAgent::NoError), @@ -284,23 +260,20 @@ void QBluetoothServiceDiscoveryAgentPrivate::runSdpScan( return; } - qCDebug(QT_BT_BLUEZ) << "SDP search a success. Iterating results" << sdpResults; QStringList xmlRecords; - // process the results - for ( ; sdpResults; sdpResults = sdpResults->next) { - sdp_record_t *record = (sdp_record_t *) sdpResults->data; + int size, index = 0; + const QByteArray output = QByteArray::fromBase64(process.readAll()); + const char *data = output.constData(); - QByteArray xml = parseSdpRecord(record); - if (xml.isEmpty()) - continue; - - //qDebug() << xml; - xmlRecords.append(QString::fromUtf8(xml)); + // separate the individial SDP records + // each record starts with 4 byte size indicator + while (index < output.size()) { + memcpy(&size, &data[index], sizeof(int)); + xmlRecords.append(QString::fromUtf8(output.mid(index+sizeof(int), size))); + index += sizeof(int) + size; } - sdp_close(session); - QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection, Q_ARG(QBluetoothServiceDiscoveryAgent::Error, QBluetoothServiceDiscoveryAgent::NoError), diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index f2969da8..d499b6ad 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -98,6 +98,8 @@ void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &addr Q_Q(QBluetoothSocket); Q_UNUSED(openMode); + qCDebug(QT_BT_ANDROID) << "connectToServiceConc()" << address.toString() << uuid.toString(); + if (!adapter.isValid()) { qCWarning(QT_BT_ANDROID) << "Device does not support Bluetooth"; errorString = QBluetoothSocket::tr("Device does not support Bluetooth"); @@ -157,7 +159,7 @@ void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &addr } socketObject.callMethod<void>("connect"); - if (env->ExceptionCheck() || socketObject.callMethod<jboolean>("isConnected") == JNI_FALSE) { + if (env->ExceptionCheck()) { if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); @@ -383,14 +385,11 @@ void QBluetoothSocketPrivate::inputThreadError(int errorCode) //cleanup internal objects //if it was call to local close()/abort() the objects are cleaned up already - bool stillConnected = socketObject.callMethod<jboolean>("isConnected"); - if (stillConnected) { - QAndroidJniEnvironment env; - socketObject.callMethod<void>("close"); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } + QAndroidJniEnvironment env; + socketObject.callMethod<void>("close"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); } inputStream = outputStream = remoteDevice = socketObject = QAndroidJniObject(); diff --git a/src/bluetooth/qbluetoothsocket_bluez.cpp b/src/bluetooth/qbluetoothsocket_bluez.cpp index b046998a..1807a225 100644 --- a/src/bluetooth/qbluetoothsocket_bluez.cpp +++ b/src/bluetooth/qbluetoothsocket_bluez.cpp @@ -45,16 +45,13 @@ #include "bluez/manager_p.h" #include "bluez/adapter_p.h" #include "bluez/device_p.h" -#include "bluez/bluez5_helper_p.h" #include "bluez/objectmanager_p.h" #include <QtBluetooth/QBluetoothLocalDevice> +#include "bluez/bluez_data_p.h" #include <qplatformdefs.h> #include <QtCore/QLoggingCategory> -#include <bluetooth/bluetooth.h> -#include <bluetooth/rfcomm.h> -#include <bluetooth/l2cap.h> #include <errno.h> #include <unistd.h> diff --git a/src/nfc/doc/qtnfc.qdocconf b/src/nfc/doc/qtnfc.qdocconf index 2d5b0ef5..61ed15b6 100644 --- a/src/nfc/doc/qtnfc.qdocconf +++ b/src/nfc/doc/qtnfc.qdocconf @@ -2,7 +2,6 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtNfc description = Qt NFC Reference Documentation -url = http://qt-project.org/doc/qt-$QT_VER version = $QT_VERSION examplesinstallpath = nfc diff --git a/src/src.pro b/src/src.pro index e017d34b..c5585757 100644 --- a/src/src.pro +++ b/src/src.pro @@ -17,3 +17,7 @@ qtHaveModule(quick) { imports.depends += bluetooth nfc SUBDIRS += imports } + +config_bluez:qtHaveModule(dbus) { + SUBDIRS += tools/sdpscanner +} diff --git a/src/tools/sdpscanner/main.cpp b/src/tools/sdpscanner/main.cpp new file mode 100644 index 00000000..39624976 --- /dev/null +++ b/src/tools/sdpscanner/main.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QByteArray> +#include <QtCore/QDebug> +#include <stdio.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#define RETURN_SUCCESS 0 +#define RETURN_USAGE 1 +#define RETURN_INVALPARAM 2 +#define RETURN_SDP_ERROR 3 + +void usage() +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\tsdpscanner <remote bdaddr> <local bdaddr>\n\n"); + fprintf(stderr, "Performs an SDP scan on remote device, using the SDP server\n" + "represented by the local Bluetooth device.\n"); +} + +#define BUFFER_SIZE 1024 + +static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray &xmlOutput) +{ + if (!data) + return; + + const int length = indentation*2 + 1; + QByteArray indentString(length, ' '); + + char snBuffer[BUFFER_SIZE]; + + xmlOutput.append(indentString); + + // deal with every dtd type + switch (data->dtd) { + case SDP_DATA_NIL: + xmlOutput.append("<nil/>\n"); + break; + case SDP_UINT8: + qsnprintf(snBuffer, BUFFER_SIZE, "<uint8 value=\"0x%02x\"/>\n", data->val.uint8); + xmlOutput.append(snBuffer); + break; + case SDP_UINT16: + qsnprintf(snBuffer, BUFFER_SIZE, "<uint16 value=\"0x%04x\"/>\n", data->val.uint16); + xmlOutput.append(snBuffer); + break; + case SDP_UINT32: + qsnprintf(snBuffer, BUFFER_SIZE, "<uint32 value=\"0x%08x\"/>\n", data->val.uint32); + xmlOutput.append(snBuffer); + break; + case SDP_UINT64: + qsnprintf(snBuffer, BUFFER_SIZE, "<uint64 value=\"0x%016x\"/>\n", data->val.uint64); + xmlOutput.append(snBuffer); + break; + case SDP_UINT128: + xmlOutput.append("<uint128 value=\"0x"); + for (int i = 0; i < 16; i++) + ::sprintf(&snBuffer[i * 2], "%02x", data->val.uint128.data[i]); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_INT8: + qsnprintf(snBuffer, BUFFER_SIZE, "<int8 value=\"%d\"/>/n", data->val.int8); + xmlOutput.append(snBuffer); + break; + case SDP_INT16: + qsnprintf(snBuffer, BUFFER_SIZE, "<int16 value=\"%d\"/>/n", data->val.int16); + xmlOutput.append(snBuffer); + break; + case SDP_INT32: + qsnprintf(snBuffer, BUFFER_SIZE, "<int32 value=\"%d\"/>/n", data->val.int32); + xmlOutput.append(snBuffer); + break; + case SDP_INT64: + qsnprintf(snBuffer, BUFFER_SIZE, "<int64 value=\"%d\"/>/n", data->val.int64); + xmlOutput.append(snBuffer); + break; + case SDP_INT128: + xmlOutput.append("<int128 value=\"0x"); + for (int i = 0; i < 16; i++) + ::sprintf(&snBuffer[i * 2], "%02x", data->val.int128.data[i]); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_UUID_UNSPEC: + break; + case SDP_UUID16: + case SDP_UUID32: + xmlOutput.append("<uuid value=\"0x"); + sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_UUID128: + xmlOutput.append("<uuid value=\""); + sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + case SDP_TEXT_STR_UNSPEC: + break; + case SDP_TEXT_STR8: + case SDP_TEXT_STR16: + case SDP_TEXT_STR32: + { + xmlOutput.append("<text "); + QByteArray text = QByteArray::fromRawData(data->val.str, data->unitSize); + + bool hasNonPrintableChar = false; + for (int i = 0; i < text.count() && !hasNonPrintableChar; i++) { + if (!isprint(text[i])) { + hasNonPrintableChar = true; + break; + } + } + + if (hasNonPrintableChar) { + xmlOutput.append("encoding=\"hex\" value=\""); + xmlOutput.append(text.toHex()); + } else { + text.replace("&", "&"); + text.replace("<", "<"); + text.replace(">", ">"); + text.replace("\"", """); + + xmlOutput.append("value=\""); + xmlOutput.append(text); + } + + xmlOutput.append("\"/>\n"); + break; + } + case SDP_BOOL: + if (data->val.uint8) + xmlOutput.append("<boolean value=\"true\"/>\n"); + else + xmlOutput.append("<boolean value=\"false\"/>\n"); + break; + case SDP_SEQ_UNSPEC: + break; + case SDP_SEQ8: + case SDP_SEQ16: + case SDP_SEQ32: + xmlOutput.append("<sequence>\n"); + parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput); + xmlOutput.append(indentString); + xmlOutput.append("</sequence>\n"); + break; + case SDP_ALT_UNSPEC: + break; + case SDP_ALT8: + case SDP_ALT16: + case SDP_ALT32: + xmlOutput.append("<alternate>\n"); + parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput); + xmlOutput.append(indentString); + xmlOutput.append("</alternate>\n"); + break; + case SDP_URL_STR_UNSPEC: + break; + case SDP_URL_STR8: + case SDP_URL_STR16: + case SDP_URL_STR32: + strncpy(snBuffer, data->val.str, data->unitSize - 1); + xmlOutput.append("<url value=\""); + xmlOutput.append(snBuffer); + xmlOutput.append("\"/>\n"); + break; + default: + fprintf(stderr, "Unknown dtd type\n"); + } + + parseAttributeValues(data->next, indentation, xmlOutput); +} + +static void parseAttribute(void *value, void *extraData) +{ + sdp_data_t *data = (sdp_data_t *) value; + QByteArray *xmlOutput = static_cast<QByteArray *>(extraData); + + char buffer[BUFFER_SIZE]; + + ::qsnprintf(buffer, BUFFER_SIZE, " <attribute id=\"0x%04x\">\n", data->attrId); + xmlOutput->append(buffer); + + parseAttributeValues(data, 2, *xmlOutput); + + xmlOutput->append(" </attribute>\n"); +} + +// the resulting xml output is based on the already used xml parser +QByteArray parseSdpRecord(sdp_record_t *record) +{ + if (!record || !record->attrlist) + return QByteArray(); + + QByteArray xmlOutput; + + xmlOutput.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<record>\n"); + + sdp_list_foreach(record->attrlist, parseAttribute, &xmlOutput); + xmlOutput.append("</record>"); + + return xmlOutput; +} + + +int main(int argc, char **argv) +{ + if (argc != 3) { + usage(); + return RETURN_USAGE; + } + + fprintf(stderr, "SDP for %s %s\n", argv[1], argv[2]); + + bdaddr_t remote; + bdaddr_t local; + int result = str2ba(argv[1], &remote); + if (result < 0) { + fprintf(stderr, "Invalid remote address: %s\n", argv[1]); + return RETURN_INVALPARAM; + } + + result = str2ba(argv[2], &local); + if (result < 0) { + fprintf(stderr, "Invalid local address: %s\n", argv[2]); + return RETURN_INVALPARAM; + } + + sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY); + if (!session) { + //try one more time if first time failed + session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY); + } + + if (!session) { + fprintf(stderr, "Cannot establish sdp session\n"); + return RETURN_SDP_ERROR; + } + + // set the filter for service matches + uuid_t publicBrowseGroupUuid; + sdp_uuid16_create(&publicBrowseGroupUuid, PUBLIC_BROWSE_GROUP); + sdp_list_t *serviceFilter; + serviceFilter = sdp_list_append(0, &publicBrowseGroupUuid); + + uint32_t attributeRange = 0x0000ffff; //all attributes + sdp_list_t *attributes; + attributes = sdp_list_append(0, &attributeRange); + + sdp_list_t *sdpResults, *previous; + result = sdp_service_search_attr_req(session, serviceFilter, + SDP_ATTR_REQ_RANGE, + attributes, &sdpResults); + sdp_list_free(attributes, 0); + sdp_list_free(serviceFilter, 0); + + if (result != 0) { + fprintf(stderr, "sdp_service_search_attr_req failed\n"); + sdp_close(session); + return RETURN_SDP_ERROR; + } + + char sizeField[sizeof(int)]; + while (sdpResults) { + sdp_record_t *record = (sdp_record_t *) sdpResults->data; + + QByteArray xml = parseSdpRecord(record); + if (xml.isEmpty()) + continue; + + //endianness doesn't matter since same machine + int sz = xml.size(); + memcpy(&sizeField, &sz, sizeof(int)); + xml.prepend(QByteArray(sizeField, sizeof(int))); + + printf("%s", xml.toBase64().constData()); + + previous = sdpResults; + sdpResults = sdpResults->next; + free(previous); + sdp_record_free(record); + + } + + sdp_close(session); + + return RETURN_SUCCESS; +} diff --git a/src/tools/sdpscanner/sdpscanner.pro b/src/tools/sdpscanner/sdpscanner.pro new file mode 100644 index 00000000..6bf0a96c --- /dev/null +++ b/src/tools/sdpscanner/sdpscanner.pro @@ -0,0 +1,24 @@ +TEMPLATE = app +TARGET = sdpscanner + +QT = core + +SOURCES = main.cpp + +CONFIG += link_pkgconfig +PKGCONFIG_PRIVATE += bluez + +load(qt_tool) + +linux-*: { + # bluetooth.h is not standards compliant + contains(QMAKE_CXXFLAGS, -std=c++0x) { + QMAKE_CXXFLAGS -= -std=c++0x + QMAKE_CXXFLAGS += -std=gnu++0x + CONFIG -= c++11 + } + c++11 { + CONFIG -= c++11 + QMAKE_CXXFLAGS += -std=gnu++0x + } +} diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp index df23c859..6b7027d0 100644 --- a/tests/bttestui/btlocaldevice.cpp +++ b/tests/bttestui/btlocaldevice.cpp @@ -268,6 +268,8 @@ void BtLocalDevice::stopDiscovery() void BtLocalDevice::startServiceDiscovery(bool isMinimalDiscovery) { if (serviceAgent) { + serviceAgent->setRemoteAddress(QBluetoothAddress()); + qDebug() << "###### Starting service discovery process"; serviceAgent->start(isMinimalDiscovery ? QBluetoothServiceDiscoveryAgent::MinimalDiscovery @@ -275,6 +277,24 @@ void BtLocalDevice::startServiceDiscovery(bool isMinimalDiscovery) } } +void BtLocalDevice::startTargettedServiceDiscovery() +{ + if (serviceAgent) { + const QBluetoothAddress baddr(BTCHAT_DEVICE_ADDR); + qDebug() << "###### Starting service discovery on" + << baddr.toString(); + if (baddr.isNull()) + return; + + if (!serviceAgent->setRemoteAddress(baddr)) { + qWarning() << "###### Cannot set remote address. Aborting"; + return; + } + + serviceAgent->start(); + } +} + void BtLocalDevice::stopServiceDiscovery() { if (serviceAgent) { @@ -409,6 +429,12 @@ void BtLocalDevice::abortSocket() qDebug() << "###### Disconnecting socket"; socket->abort(); } + + if (!serverSockets.isEmpty()) { + qDebug() << "###### Closing server sockets"; + foreach (QBluetoothSocket *s, serverSockets) + s->abort(); + } } void BtLocalDevice::socketConnected() diff --git a/tests/bttestui/btlocaldevice.h b/tests/bttestui/btlocaldevice.h index a9571ecc..a0c020cc 100644 --- a/tests/bttestui/btlocaldevice.h +++ b/tests/bttestui/btlocaldevice.h @@ -87,6 +87,7 @@ public slots: //QBluetoothServiceDiscoveryAgent void startServiceDiscovery(bool isMinimalDiscovery); + void startTargettedServiceDiscovery(); void stopServiceDiscovery(); void serviceDiscovered(const QBluetoothServiceInfo &info); void leServiceDiscovered(const QLowEnergyServiceInfo &leInfo); diff --git a/tests/bttestui/main.qml b/tests/bttestui/main.qml index d53aa4e1..93d943f2 100644 --- a/tests/bttestui/main.qml +++ b/tests/bttestui/main.qml @@ -168,6 +168,11 @@ Rectangle { onClicked: device.startServiceDiscovery(false) } Button { + id: startRemoteSDiscBtn + buttonText: "StartRemSDisc" + onClicked: device.startTargettedServiceDiscovery() + } + Button { buttonText: "StopSDisc" onClicked: device.stopServiceDiscovery(); } |