Source

Target

Commits (18)
Showing with 298 additions and 190 deletions
Qt 5.4.1 is a bug-fix release. It maintains both forward and backward
compatibility (source and binary) with Qt 5.4.0.
For more details, refer to the online documentation included in this
distribution. The documentation is also available online:
http://doc.qt.io/qt-5.4
The Qt version 5.4 series is binary compatible with the 5.3.x series.
Applications compiled for 5.3 will continue to run with 5.4.
Some of the changes listed in this file include issue tracking numbers
corresponding to tasks in the Qt Bug Tracker:
http://bugreports.qt.io/
Each of these identifiers can be entered in the bug tracker to obtain more
information about a particular change.
****************************************************************************
* Library *
****************************************************************************
QtBluetooth
-----------
- General:
* Extended documentation with regards to Bluetooth Low Energy. The
affected classes were QLowEnergyController and QLowEnergyService.
* LowEnergyScanner and chat examples improved.
- QBluetoothServer:
* [QTBUG-43806] Fixed SDP registration of PublicBrowseGroup in BlueZ 5.x.
- QLowEnergyController:
* Fixed blocking of ATT command processing due to a reconnect to the target
device.
......@@ -101,8 +101,10 @@ void ChatServer::startServer(const QBluetoothAddress& localAdapter)
//! [Service UUID set]
//! [Service Discoverability]
QBluetoothServiceInfo::Sequence publicBrowse;
publicBrowse << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList,
QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
publicBrowse);
//! [Service Discoverability]
//! [Protocol descriptor list]
......
......@@ -41,9 +41,11 @@
#include "chat.h"
#include <QApplication>
//#include <QtCore/QLoggingCategory>
int main(int argc, char *argv[])
{
//QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
QApplication app(argc, argv);
Chat d;
......
......@@ -136,6 +136,8 @@ void RemoteSelector::on_remoteDevices_itemActivated(QListWidgetItem *item)
{
qDebug() << "got click" << item->text();
m_service = m_discoveredServices.value(item);
if (m_discoveryAgent->isActive())
m_discoveryAgent->stop();
accept();
}
......
......@@ -50,8 +50,8 @@ LocalDeviceBroadcastReceiver::LocalDeviceBroadcastReceiver(QObject *parent) :
addAction(valueForStaticField(JavaNames::BluetoothAdapter, JavaNames::ActionScanModeChanged));
addAction(valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ActionAclConnected));
addAction(valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ActionAclDisconnected));
if (QtAndroidPrivate::androidSdkVersion() >= 19)
addAction(valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ActionPairingRequest)); //API 19
if (QtAndroidPrivate::androidSdkVersion() >= 15)
addAction(valueForStaticField(JavaNames::BluetoothDevice, JavaNames::ActionPairingRequest)); //API 15
//cache integer values for host & bonding mode
//don't use the java fields directly but refer to them by name
......
......@@ -45,16 +45,16 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
typedef enum Bluez5TestResultType
{
Unknown,
Bluez4,
Bluez5
BluezVersionUnknown,
BluezVersion4,
BluezVersion5
} Bluez5TestResult;
Q_GLOBAL_STATIC_WITH_ARGS(Bluez5TestResult, bluezVersion, (Bluez5TestResult::Unknown));
Q_GLOBAL_STATIC_WITH_ARGS(Bluez5TestResult, bluezVersion, (BluezVersionUnknown));
bool isBluez5()
{
if (*bluezVersion() == Bluez5TestResultType::Unknown) {
if (*bluezVersion() == BluezVersionUnknown) {
OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"),
QStringLiteral("/"),
QDBusConnection::systemBus());
......@@ -65,15 +65,15 @@ bool isBluez5()
QDBusPendingReply<ManagedObjectList> reply = manager.GetManagedObjects();
reply.waitForFinished();
if (reply.isError()) {
*bluezVersion() = Bluez5TestResultType::Bluez4;
*bluezVersion() = BluezVersion4;
qCDebug(QT_BT_BLUEZ) << "Bluez 4 detected.";
} else {
*bluezVersion() = Bluez5TestResultType::Bluez5;
*bluezVersion() = BluezVersion5;
qCDebug(QT_BT_BLUEZ) << "Bluez 5 detected.";
}
}
return (*bluezVersion() == Bluez5TestResultType::Bluez5);
return (*bluezVersion() == BluezVersion5);
}
struct AdapterData
......
......@@ -75,8 +75,8 @@
#define BT_SECURITY 4
struct bt_security {
uint8_t level;
uint8_t key_size;
quint8 level;
quint8 key_size;
};
#define BT_SECURITY_SDP 0
#define BT_SECURITY_LOW 1
......@@ -162,14 +162,14 @@ static inline void ntoh128(const quint128 *src, quint128 *dst)
dst->data[15 - i] = src->data[i];
}
static inline uint16_t bt_get_le16(const void *ptr)
static inline quint16 bt_get_le16(const void *ptr)
{
return bt_get_unaligned((const uint16_t *) ptr);
return bt_get_unaligned((const quint16 *) ptr);
}
#elif __BYTE_ORDER == __BIG_ENDIAN
static inline uint16_t bt_get_le16(const void *ptr)
static inline quint16 bt_get_le16(const void *ptr)
{
return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
return bswap_16(bt_get_unaligned((const quint16 *) ptr));
}
static inline void btoh128(const quint128 *src, quint128 *dst)
......@@ -213,79 +213,79 @@ struct sockaddr_hci {
};
struct hci_dev_req {
uint16_t dev_id;
uint32_t dev_opt;
quint16 dev_id;
quint32 dev_opt;
};
struct hci_dev_list_req {
uint16_t dev_num;
quint16 dev_num;
struct hci_dev_req dev_req[0];
};
struct hci_dev_stats {
uint32_t err_rx;
uint32_t err_tx;
uint32_t cmd_tx;
uint32_t evt_rx;
uint32_t acl_tx;
uint32_t acl_rx;
uint32_t sco_tx;
uint32_t sco_rx;
uint32_t byte_rx;
uint32_t byte_tx;
quint32 err_rx;
quint32 err_tx;
quint32 cmd_tx;
quint32 evt_rx;
quint32 acl_tx;
quint32 acl_rx;
quint32 sco_tx;
quint32 sco_rx;
quint32 byte_rx;
quint32 byte_tx;
};
struct hci_dev_info {
uint16_t dev_id;
quint16 dev_id;
char name[8];
bdaddr_t bdaddr;
uint32_t flags;
uint8_t type;
quint32 flags;
quint8 type;
uint8_t features[8];
quint8 features[8];
uint32_t pkt_type;
uint32_t link_policy;
uint32_t link_mode;
quint32 pkt_type;
quint32 link_policy;
quint32 link_mode;
uint16_t acl_mtu;
uint16_t acl_pkts;
uint16_t sco_mtu;
uint16_t sco_pkts;
quint16 acl_mtu;
quint16 acl_pkts;
quint16 sco_mtu;
quint16 sco_pkts;
struct hci_dev_stats stat;
};
struct hci_conn_info {
uint16_t handle;
quint16 handle;
bdaddr_t bdaddr;
uint8_t type;
uint8_t out;
uint16_t state;
uint32_t link_mode;
quint8 type;
quint8 out;
quint16 state;
quint32 link_mode;
};
struct hci_conn_list_req {
uint16_t dev_id;
uint16_t conn_num;
quint16 dev_id;
quint16 conn_num;
struct hci_conn_info conn_info[0];
};
struct hci_filter {
uint32_t type_mask;
uint32_t event_mask[2];
uint16_t opcode;
quint32 type_mask;
quint32 event_mask[2];
quint16 opcode;
};
static inline void hci_set_bit(int nr, void *addr)
{
*((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31));
*((quint32 *) addr + (nr >> 5)) |= (1 << (nr & 31));
}
static inline void hci_clear_bit(int nr, void *addr)
{
*((uint32_t *) addr + (nr >> 5)) &= ~(1 << (nr & 31));
*((quint32 *) addr + (nr >> 5)) &= ~(1 << (nr & 31));
}
static inline void hci_filter_clear(struct hci_filter *f)
{
......@@ -317,16 +317,16 @@ static inline void hci_filter_all_events(struct hci_filter *f)
}
typedef struct {
uint8_t evt;
uint8_t plen;
quint8 evt;
quint8 plen;
} __attribute__ ((packed)) hci_event_hdr;
#define HCI_EVENT_HDR_SIZE 2
#define EVT_ENCRYPT_CHANGE 0x08
typedef struct {
uint8_t status;
uint16_t handle;
uint8_t encrypt;
quint8 status;
quint16 handle;
quint8 encrypt;
} __attribute__ ((packed)) evt_encrypt_change;
#define EVT_ENCRYPT_CHANGE_SIZE 4
......
......@@ -38,6 +38,7 @@
#include <QtCore/QLoggingCategory>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
......
......@@ -45,6 +45,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qdebug.h>
#include <algorithm>
......@@ -206,31 +207,33 @@ using namespace QT_NAMESPACE;
}
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_6_0)
const quint128 qtUuidData(deviceUuid.toUInt128());
// STATIC_ASSERT on sizes would be handy!
uuid_t uuidData = {};
std::copy(qtUuidData.data, qtUuidData.data + 16, uuidData);
const ObjCScopedPointer<NSUUID> nsUuid([[NSUUID alloc] initWithUUIDBytes:uuidData]);
if (!nsUuid) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate NSUUID identifier";
return QLowEnergyController::ConnectionError;
}
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_6_0)) {
const quint128 qtUuidData(deviceUuid.toUInt128());
// STATIC_ASSERT on sizes would be handy!
uuid_t uuidData = {};
std::copy(qtUuidData.data, qtUuidData.data + 16, uuidData);
const ObjCScopedPointer<NSUUID> nsUuid([[NSUUID alloc] initWithUUIDBytes:uuidData]);
if (!nsUuid) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to allocate NSUUID identifier";
return QLowEnergyController::ConnectionError;
}
[uuids addObject:nsUuid];
[uuids addObject:nsUuid];
// With the latest CoreBluetooth, we can synchronously retrive peripherals:
QT_BT_MAC_AUTORELEASEPOOL;
NSArray *const peripherals = [manager retrievePeripheralsWithIdentifiers:uuids];
if (!peripherals || peripherals.count != 1) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to retrive a peripheral";
return QLowEnergyController::UnknownRemoteDeviceError;
}
// With the latest CoreBluetooth, we can synchronously retrive peripherals:
QT_BT_MAC_AUTORELEASEPOOL;
NSArray *const peripherals = [manager retrievePeripheralsWithIdentifiers:uuids];
if (!peripherals || peripherals.count != 1) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to retrive a peripheral";
return QLowEnergyController::UnknownRemoteDeviceError;
}
peripheral = [static_cast<CBPeripheral *>([peripherals objectAtIndex:0]) retain];
[self connectToPeripheral];
peripheral = [static_cast<CBPeripheral *>([peripherals objectAtIndex:0]) retain];
[self connectToPeripheral];
return QLowEnergyController::NoError;
#else
return QLowEnergyController::NoError;
}
#endif
OSXBluetooth::CFStrongReference<CFUUIDRef> cfUuid(OSXBluetooth::cf_uuid(deviceUuid));
if (!cfUuid) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to create CFUUID object";
......@@ -243,7 +246,6 @@ using namespace QT_NAMESPACE;
[manager retrievePeripherals:uuids];
return QLowEnergyController::NoError;
#endif
}
- (void)connectToPeripheral
......@@ -270,10 +272,12 @@ using namespace QT_NAMESPACE;
return false;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_7_0)
return peripheral.state == CBPeripheralStateConnected;
#else
return peripheral.isConnected;
using OSXBluetooth::qt_OS_limit;
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0))
return peripheral.state == CBPeripheralStateConnected;
#endif
return peripheral.isConnected;
}
- (void)disconnectFromDevice
......
......@@ -43,6 +43,7 @@
#include "osxbtutility_p.h"
#include <QtCore/qloggingcategory.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
......@@ -92,10 +93,13 @@ using namespace QT_NAMESPACE;
- (void)dealloc
{
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_NA)
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
// Stop also sets a delegate to nil (Apple's docs).
// 10.9 only.
[m_pairing stop];
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
[m_pairing stop];
else
[m_pairing setDelegate:nil];
#else
[m_pairing setDelegate:nil];
#endif
......
......@@ -45,6 +45,7 @@
#include "osxbtutility_p.h"
#include <QtCore/qloggingcategory.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qdebug.h>
#include "corebluetoothwrapper_p.h"
......@@ -57,7 +58,6 @@ LEDeviceInquiryDelegate::~LEDeviceInquiryDelegate()
{
}
// TODO: check versions!
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_6_0)
QBluetoothUuid qt_uuid(NSUUID *nsUuid)
......@@ -72,7 +72,7 @@ QBluetoothUuid qt_uuid(NSUUID *nsUuid)
return QBluetoothUuid(qtUuidData);
}
#else
#endif
QBluetoothUuid qt_uuid(CFUUIDRef uuid)
{
......@@ -104,8 +104,6 @@ StringStrongReference uuid_as_nsstring(CFUUIDRef uuid)
return StringStrongReference((NSString *)cfStr, false);
}
#endif
}
......@@ -301,17 +299,20 @@ using namespace QT_NAMESPACE;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_6_0)
if (!peripheral.identifier) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "peripheral without NSUUID";
return;
}
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_6_0)) {
if (!peripheral.identifier) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "peripheral without NSUUID";
return;
}
if (![peripherals objectForKey:peripheral.identifier]) {
[peripherals setObject:peripheral forKey:peripheral.identifier];
const QBluetoothUuid deviceUuid(OSXBluetooth::qt_uuid(peripheral.identifier));
delegate->LEdeviceFound(peripheral, deviceUuid, advertisementData, RSSI);
if (![peripherals objectForKey:peripheral.identifier]) {
[peripherals setObject:peripheral forKey:peripheral.identifier];
const QBluetoothUuid deviceUuid(OSXBluetooth::qt_uuid(peripheral.identifier));
delegate->LEdeviceFound(peripheral, deviceUuid, advertisementData, RSSI);
}
return;
}
#else
#endif
if (!peripheral.UUID) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "peripheral without UUID";
return;
......@@ -323,8 +324,6 @@ using namespace QT_NAMESPACE;
const QBluetoothUuid deviceUuid(OSXBluetooth::qt_uuid(peripheral.UUID));
delegate->LEdeviceFound(peripheral, deviceUuid, advertisementData, RSSI);
}
#endif
}
- (bool)isActive
......
......@@ -44,6 +44,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qglobal.h>
#include <Foundation/Foundation.h>
......@@ -291,6 +292,17 @@ QByteArray qt_bytearray(NSData *data);
QByteArray qt_bytearray(NSObject *data);
ObjCStrongReference<NSData> data_from_bytearray(const QByteArray & qtData);
inline QSysInfo::MacVersion qt_OS_limit(QSysInfo::MacVersion osxVersion, QSysInfo::MacVersion iosVersion)
{
#ifdef Q_OS_OSX
Q_UNUSED(iosVersion)
return osxVersion;
#else
Q_UNUSED(osxVersion)
return iosVersion;
#endif
}
} // namespace OSXBluetooth
// Logging category for both OS X and iOS.
......
......@@ -376,9 +376,9 @@ void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pai
return;
}
// BluetoothDevice::createBond() requires Android API 19
if (QtAndroidPrivate::androidSdkVersion() < 19 || !d_ptr->adapter()) {
qCWarning(QT_BT_ANDROID) << "Unable to pair: requires Android API 19+";
// BluetoothDevice::createBond() requires Android API 15
if (QtAndroidPrivate::androidSdkVersion() < 15 || !d_ptr->adapter()) {
qCWarning(QT_BT_ANDROID) << "Unable to pair: requires Android API 15+";
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QBluetoothLocalDevice::Error,
QBluetoothLocalDevice::PairingError));
......
......@@ -345,6 +345,7 @@ void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pai
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QBluetoothLocalDevice::Error,
QBluetoothLocalDevice::PairingError));
delete device;
return;
}
delete device;
......@@ -368,6 +369,7 @@ void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pai
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QBluetoothLocalDevice::Error,
QBluetoothLocalDevice::PairingError));
delete device;
return;
}
delete device;
......@@ -579,15 +581,21 @@ QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
QDBusPendingReply<QVariantMap> deviceReply = device->GetProperties();
deviceReply.waitForFinished();
if (deviceReply.isError())
if (deviceReply.isError()) {
delete device;
return Unpaired;
}
QVariantMap map = deviceReply.value();
if (map.value(QStringLiteral("Trusted")).toBool() && map.value(QStringLiteral("Paired")).toBool())
if (map.value(QStringLiteral("Trusted")).toBool() && map.value(QStringLiteral("Paired")).toBool()) {
delete device;
return AuthorizedPaired;
else if (map.value(QStringLiteral("Paired")).toBool())
} else if (map.value(QStringLiteral("Paired")).toBool()) {
delete device;
return Paired;
}
delete device;
} else if (d_ptr->adapterBluez5) {
QDBusPendingReply<ManagedObjectList> reply = d_ptr->managerBluez5->GetManagedObjects();
......
......@@ -204,8 +204,10 @@ QBluetoothServiceInfo QBluetoothServer::listen(const QBluetoothUuid &uuid, const
//! [listen]
QBluetoothServiceInfo serviceInfo;
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceName, serviceName);
QBluetoothServiceInfo::Sequence browseSequence;
browseSequence << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList,
QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
browseSequence);
QBluetoothServiceInfo::Sequence classId;
classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
......
......@@ -183,6 +183,11 @@ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoot
QBluetoothServiceDiscoveryAgent::~QBluetoothServiceDiscoveryAgent()
{
if (isActive()) {
disconnect(); //don't emit any signals due to stop()
stop();
}
delete d_ptr;
}
......
......@@ -42,6 +42,10 @@
#include <QtBluetooth/QBluetoothUuid>
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
#ifdef QT_BLUEZ_BLUETOOTH
#include <QtCore/qprocess.h>
#endif
QT_BEGIN_NAMESPACE
class QBluetoothAddress;
......@@ -108,7 +112,7 @@ private:
#ifdef QT_BLUEZ_BLUETOOTH
Q_PRIVATE_SLOT(d_func(), void _q_discoveredServices(QDBusPendingCallWatcher*))
Q_PRIVATE_SLOT(d_func(), void _q_createdDevice(QDBusPendingCallWatcher*))
Q_PRIVATE_SLOT(d_func(), void _q_finishSdpScan(QBluetoothServiceDiscoveryAgent::Error, const QString &, const QStringList &))
Q_PRIVATE_SLOT(d_func(), void _q_sdpScannerDone(int,QProcess::ExitStatus))
#endif
#ifdef QT_ANDROID_BLUETOOTH
Q_PRIVATE_SLOT(d_func(), void _q_processFetchedUuids(const QBluetoothAddress &address,
......
......@@ -65,7 +65,7 @@ static inline void convertAddress(quint64 from, quint8 (&to)[6])
QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &deviceAdapter)
: error(QBluetoothServiceDiscoveryAgent::NoError), m_deviceAdapterAddress(deviceAdapter), state(Inactive), deviceDiscoveryAgent(0),
mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), singleDevice(false),
manager(0), managerBluez5(0), adapter(0), device(0)
manager(0), managerBluez5(0), adapter(0), device(0), sdpScannerProcess(0)
{
if (isBluez5()) {
managerBluez5 = new OrgFreedesktopDBusObjectManagerInterface(
......@@ -136,6 +136,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr
}
// Bluez 5
void QBluetoothServiceDiscoveryAgentPrivate::startBluez5(const QBluetoothAddress &address)
{
Q_Q(QBluetoothServiceDiscoveryAgent);
......@@ -185,81 +186,64 @@ void QBluetoothServiceDiscoveryAgentPrivate::startBluez5(const QBluetoothAddress
if (DiscoveryMode() == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) {
performMinimalServiceDiscovery(address);
} else {
// we need to run the discovery in a different thread
// as it involves blocking calls
QtConcurrent::run(this, &QBluetoothServiceDiscoveryAgentPrivate::runSdpScan,
address, QBluetoothAddress(adapter.address()));
runExternalSdpScan(address, QBluetoothAddress(adapter.address()));
}
}
/*
* 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.
*
/* Bluez 5
* 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(
void QBluetoothServiceDiscoveryAgentPrivate::runExternalSdpScan(
const QBluetoothAddress &remoteAddress, const QBluetoothAddress localAddress)
{
Q_Q(QBluetoothServiceDiscoveryAgent);
const QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
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 find sdpscanner")),
Q_ARG(QStringList, QStringList()));
qCWarning(QT_BT_BLUEZ) << "Cannot find sdpscanner:"
<< fileInfo.canonicalFilePath();
return;
if (!sdpScannerProcess) {
const QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
QFileInfo fileInfo(binPath, QStringLiteral("sdpscanner"));
if (!fileInfo.exists() || !fileInfo.isExecutable()) {
_q_finishSdpScan(QBluetoothServiceDiscoveryAgent::InputOutputError,
QBluetoothServiceDiscoveryAgent::tr("Unable to find sdpscanner"),
QStringList());
qCWarning(QT_BT_BLUEZ) << "Cannot find sdpscanner:"
<< fileInfo.canonicalFilePath();
return;
}
sdpScannerProcess = new QProcess(q);
sdpScannerProcess->setReadChannel(QProcess::StandardOutput);
sdpScannerProcess->setProgram(fileInfo.canonicalFilePath());
q->connect(sdpScannerProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
q, SLOT(_q_sdpScannerDone(int,QProcess::ExitStatus)));
}
QStringList arguments;
arguments << remoteAddress.toString() << localAddress.toString();
QByteArray output;
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedErrorChannel);
process.setReadChannel(QProcess::StandardOutput);
process.start(fileInfo.canonicalFilePath(), arguments);
if (process.waitForStarted(-1)) {
while (process.waitForReadyRead(-1))
output += process.readAllStandardOutput();
}
process.waitForFinished();
sdpScannerProcess->setArguments(arguments);
sdpScannerProcess->start();
}
if (process.exitStatus() != QProcess::NormalExit
|| process.exitCode() != 0) {
qCWarning(QT_BT_BLUEZ) << "SDP scan failure"
<< process.exitStatus() << process.exitCode()
<< remoteAddress;
// Bluez 5
void QBluetoothServiceDiscoveryAgentPrivate::_q_sdpScannerDone(int exitCode, QProcess::ExitStatus status)
{
if (status != QProcess::NormalExit || exitCode != 0) {
qCWarning(QT_BT_BLUEZ) << "SDP scan failure" << status << exitCode;
if (singleDevice) {
QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection,
Q_ARG(QBluetoothServiceDiscoveryAgent::Error,
QBluetoothServiceDiscoveryAgent::InputOutputError),
Q_ARG(QString,
QBluetoothServiceDiscoveryAgent::tr("Unable to perform SDP scan")),
Q_ARG(QStringList, QStringList()));
_q_finishSdpScan(QBluetoothServiceDiscoveryAgent::InputOutputError,
QBluetoothServiceDiscoveryAgent::tr("Unable to perform SDP scan"),
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()));
_q_finishSdpScan(QBluetoothServiceDiscoveryAgent::NoError, QString(), QStringList());
}
return;
}
QStringList xmlRecords;
const QByteArray output = sdpScannerProcess->readAllStandardOutput();
const QString decodedData = QString::fromUtf8(QByteArray::fromBase64(output));
// split the various xml docs up
......@@ -276,13 +260,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::runSdpScan(
} while ( start != -1);
}
QMetaObject::invokeMethod(q, "_q_finishSdpScan", Qt::QueuedConnection,
Q_ARG(QBluetoothServiceDiscoveryAgent::Error,
QBluetoothServiceDiscoveryAgent::NoError),
Q_ARG(QString, QString()),
Q_ARG(QStringList, xmlRecords));
_q_finishSdpScan(QBluetoothServiceDiscoveryAgent::NoError, QString(), xmlRecords);
}
// Bluez 5
void QBluetoothServiceDiscoveryAgentPrivate::_q_finishSdpScan(QBluetoothServiceDiscoveryAgent::Error errorCode,
const QString &errorDescription,
const QStringList &xmlRecords)
......@@ -352,8 +333,19 @@ void QBluetoothServiceDiscoveryAgentPrivate::stop()
Q_ASSERT(!device);
}
discoveredDevices.clear();
setDiscoveryState(Inactive);
// must happen after discoveredDevices.clear() above to avoid retrigger of next scan
// while waitForFinished() is waiting
if (sdpScannerProcess) { // Bluez 5
if (sdpScannerProcess->state() != QProcess::NotRunning) {
sdpScannerProcess->kill();
sdpScannerProcess->waitForFinished();
}
}
Q_Q(QBluetoothServiceDiscoveryAgent);
emit q->canceled();
}
......@@ -591,6 +583,7 @@ QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(
return serviceInfo;
}
// Bluez 5
void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress)
{
if (foundHostAdapterPath.isEmpty()) {
......
......@@ -58,6 +58,7 @@ class OrgBluezManagerInterface;
class OrgBluezAdapterInterface;
class OrgBluezDeviceInterface;
class OrgFreedesktopDBusObjectManagerInterface;
#include <QtCore/qprocess.h>
QT_BEGIN_NAMESPACE
class QDBusPendingCallWatcher;
......@@ -127,6 +128,7 @@ public:
void _q_discoverGattCharacteristics(QDBusPendingCallWatcher *watcher);
void _q_discoveredGattCharacteristic(QDBusPendingCallWatcher *watcher);
*/
void _q_sdpScannerDone(int exitCode, QProcess::ExitStatus status);
void _q_finishSdpScan(QBluetoothServiceDiscoveryAgent::Error errorCode,
const QString &errorDescription,
const QStringList &xmlRecords);
......@@ -147,8 +149,9 @@ private:
#ifdef QT_BLUEZ_BLUETOOTH
void startBluez5(const QBluetoothAddress &address);
void runSdpScan(const QBluetoothAddress &remoteAddress,
void runExternalSdpScan(const QBluetoothAddress &remoteAddress,
const QBluetoothAddress localAddress);
void sdpScannerDone(int exitCode, QProcess::ExitStatus exitStatus);
QVariant readAttributeValue(QXmlStreamReader &xml);
QBluetoothServiceInfo parseServiceXml(const QString& xml);
void performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress);
......@@ -195,6 +198,7 @@ private:
OrgFreedesktopDBusObjectManagerInterface *managerBluez5;
OrgBluezAdapterInterface *adapter;
OrgBluezDeviceInterface *device;
QProcess *sdpScannerProcess;
#endif
#ifdef QT_ANDROID_BLUETOOTH
......
......@@ -47,6 +47,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qvariant.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qglobal.h>
#include <QtCore/qmutex.h>
#include <QtCore/qmap.h>
......@@ -66,7 +67,11 @@ namespace {
class ServiceRecordRefGuard
{
public:
ServiceRecordRefGuard(IOBluetoothSDPServiceRecordRef r)
ServiceRecordRefGuard()
: recordRef(Q_NULLPTR)
{
}
explicit ServiceRecordRefGuard(IOBluetoothSDPServiceRecordRef r)
: recordRef(r)
{
}
......@@ -76,6 +81,14 @@ public:
CFRelease(recordRef);
}
void reset(IOBluetoothSDPServiceRecordRef r)
{
if (recordRef)
CFRelease(recordRef);
// Take the ownership:
recordRef = r;
}
private:
IOBluetoothSDPServiceRecordRef recordRef;
......@@ -138,23 +151,28 @@ bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &loca
return false;
}
ServiceRecordRefGuard refGuard;
SDPRecord newRecord;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_NA)
newRecord.reset([[IOBluetoothSDPServiceRecord
publishedServiceRecordWithDictionary:serviceDict] retain]);
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9) {
newRecord.reset([[IOBluetoothSDPServiceRecord
publishedServiceRecordWithDictionary:serviceDict] retain]);
} else {
#else
IOBluetoothSDPServiceRecordRef recordRef = Q_NULLPTR;
// With ARC this will require a different cast?
const IOReturn status = IOBluetoothAddServiceDict((CFDictionaryRef)serviceDict.data(), &recordRef);
if (status != kIOReturnSuccess) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to register a service record";
return false;
}
const ServiceRecordRefGuard refGuard(recordRef);
newRecord.reset([[IOBluetoothSDPServiceRecord withSDPServiceRecordRef:recordRef] retain]);
// It's weird, but ... it's not possible to release a record ref yet.
{
#endif
IOBluetoothSDPServiceRecordRef recordRef = Q_NULLPTR;
// With ARC this will require a different cast?
const IOReturn status = IOBluetoothAddServiceDict((CFDictionaryRef)serviceDict.data(), &recordRef);
if (status != kIOReturnSuccess) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to register a service record";
return false;
}
refGuard.reset(recordRef);
newRecord.reset([[IOBluetoothSDPServiceRecord withSDPServiceRecordRef:recordRef] retain]);
// It's weird, but ... it's not possible to release a record ref yet.
}
if (!newRecord) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to register a service record";
......@@ -166,8 +184,9 @@ bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &loca
BluetoothSDPServiceRecordHandle newRecordHandle = 0;
if ([newRecord getServiceRecordHandle:&newRecordHandle] != kIOReturnSuccess) {
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to register a service record";
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_NA)
[newRecord removeServiceRecord];
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9)
[newRecord removeServiceRecord];
#endif
// With SDK < 10.9 there is no way to unregister at this point ...
return false;
......@@ -195,11 +214,16 @@ bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &loca
}
if (!configured) {
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_NA)
[newRecord removeServiceRecord];
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9) {
[newRecord removeServiceRecord];
} else {
#else
IOBluetoothRemoveServiceWithRecordHandle(newRecordHandle);
{// Just to balance braces ...
#endif
IOBluetoothRemoveServiceWithRecordHandle(newRecordHandle);
}
qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to register a service record";
return false;
}
......@@ -226,12 +250,16 @@ bool QBluetoothServiceInfoPrivate::unregisterService()
Q_ASSERT_X(serviceRecord, Q_FUNC_INFO, "service registered, but serviceRecord is nil");
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_NA)
[serviceRecord removeServiceRecord];
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9) {
[serviceRecord removeServiceRecord];
} else {
#else
// Assert on newRecordHandle? Is 0 a valid/invalid handle?
IOBluetoothRemoveServiceWithRecordHandle(serviceRecordHandle);
{
#endif
// Assert on newRecordHandle? Is 0 a valid/invalid handle?
IOBluetoothRemoveServiceWithRecordHandle(serviceRecordHandle);
}
serviceRecord.reset(nil);
......