From b303d2724720f3b8b0639d2d4394447cd5e24be4 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 9 Nov 2015 12:52:27 +0100 Subject: Standardize DST terminology. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id3fe0f89966a60f2f34ff971ad26319e95de6ae9 Reviewed-by: Topi Reiniö --- src/bluetooth/qbluetoothuuid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/qbluetoothuuid.cpp b/src/bluetooth/qbluetoothuuid.cpp index cfce871c..a7ee9c20 100644 --- a/src/bluetooth/qbluetoothuuid.cpp +++ b/src/bluetooth/qbluetoothuuid.cpp @@ -311,7 +311,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QUuid, baseUuid, ("{00000000-0000-1000-8000-00805F9B34 \value DescriptorValueChanged This characteristic is related to the Environmental Sensing Service. \value DeviceName The Device Name characteristic contains the name of the device. \value DewPoint This characteristic states the dew point in degree Celsius. - \value DSTOffset The DST Offset characteristic describes the offset employed by the daylight saving time. + \value DSTOffset The DST Offset characteristic describes the offset employed by daylight-saving time. \value Elevation The Elevation characteristic states the elevation above/below sea level. \value EmailAddress This characteristic states the email of the user. \value ExactTime256 The Exact Time 256 characteristic describes the data, day and time -- cgit v1.2.3 From 9fcc4f6ec048c98c45a05c974094de9efdef072f Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Mon, 16 Nov 2015 11:31:40 +0100 Subject: Improve QBluetoothDeviceInfo::address() documentation The address is not exposed on OS X and iOS and deviceUuid() should be used instead. Task-number: QTBUG-48746 Change-Id: I07f837ae8a01303c7d1fe27b8a8c174f288829c9 Reviewed-by: Christian Kandeler --- src/bluetooth/qbluetoothdeviceinfo.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bluetooth/qbluetoothdeviceinfo.cpp b/src/bluetooth/qbluetoothdeviceinfo.cpp index 0a66c2c2..7d5d8f0c 100644 --- a/src/bluetooth/qbluetoothdeviceinfo.cpp +++ b/src/bluetooth/qbluetoothdeviceinfo.cpp @@ -453,6 +453,12 @@ bool QBluetoothDeviceInfo::operator!=(const QBluetoothDeviceInfo &other) const /*! Returns the address of the device. + + \note On iOS and OS X this address is invalid. Instead \l deviceUuid() should be used. + Those two platforms do not expose Bluetooth addresses for found Bluetooth devices + and utilize unique device identifiers. + + \sa deviceUuid() */ QBluetoothAddress QBluetoothDeviceInfo::address() const { -- cgit v1.2.3 From 03cbe371dbd8b5eb51986d27d312813d91a8d380 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 20 Nov 2015 13:12:41 +0100 Subject: LE discovery - change a timer creation time (iOS/OS X) Create the LE scan timer when it's really needed, so that I do not have to later move it between threads if somebody scans from a different thread. Change-Id: I71669f4c1512432fc94f80446d400336d39def64 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm index ebf9352d..ad4183a7 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm @@ -44,6 +44,7 @@ #include "qbluetoothuuid.h" #include +#include #include #include #include @@ -127,7 +128,7 @@ private: typedef QList DevicesList; DevicesList discoveredDevices; - OSXBluetooth::DDATimerHandler timer; + QScopedPointer timer; }; namespace OSXBluetooth { @@ -173,8 +174,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con startPending(false), stopPending(false), lastError(QBluetoothDeviceDiscoveryAgent::NoError), - inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), - timer(this) + inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) { Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)"); @@ -267,12 +267,13 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE() // CoreBluetooth does not have a timeout. We start a timer here // and check if scan really started and if yes if we have a timeout. - timer.start([LEDeviceInquiryObjC inquiryLength]); + timer.reset(new OSXBluetooth::DDATimerHandler(this)); + timer->start([LEDeviceInquiryObjC inquiryLength]); if (![inquiryLE start]) { // We can be here only if we have some kind of resource allocation error, so we // do not emit finished, we emit error. - timer.stop(); + timer->stop(); setError(QBluetoothDeviceDiscoveryAgent::UnknownError, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED_LE)); agentState = NonActive; @@ -438,10 +439,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::checkLETimeout() if (elapsed >= timeout) [inquiryLE stop]; else - timer.start(timeout - elapsed); + timer->start(timeout - elapsed); } else { // Scan not started yet. Wait 5 seconds more. - timer.start(timeout / 2); + timer->start(timeout / 2); } } @@ -452,7 +453,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDevic Q_ASSERT_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError, Q_FUNC_INFO, "unexpected error code"); - timer.stop(); + timer->stop(); agentState = NonActive; setError(error); @@ -466,7 +467,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() // After we call startLE and before receive NotSupported, // the user can call stop (setting a pending stop). // So the same rule apply: - timer.stop(); + timer->stop(); LEdeviceInquiryFinished(); } @@ -500,7 +501,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryFinished() // The same logic as in inquiryFinished, but does not start LE scan. agentState = NonActive; - timer.stop(); + timer->stop(); if (stopPending && !startPending) { stopPending = false; -- cgit v1.2.3 From de6acd090c4e32717620863532dca4cd10214ac7 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 23 Nov 2015 12:15:14 +0100 Subject: Android: Build .jar files The build of the .jar files depends on the bluetooth and nfc modules already being built, so we need to make sure the subdirs are visited in the order specified. Change-Id: I52617babd31608be5a51c069f74de30a9d2b6a15 Task-number: QTBUG-49367 Task-number: QTBUG-49512 Reviewed-by: Alex Blasche Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/src.pro | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/src.pro b/src/src.pro index c5585757..97b32522 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,7 +1,10 @@ TEMPLATE = subdirs SUBDIRS += bluetooth nfc -android: SUBDIRS += android +android { + SUBDIRS += android + android.depends += bluetooth nfc +} contains(QT_CONFIG, private_tests) { bluetooth_doc_snippets.subdir = bluetooth/doc/snippets -- cgit v1.2.3 From dab0c9a010cf7fcd521707168cc8107b7ed6af24 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 25 Nov 2015 15:32:12 +0100 Subject: Bluetooth autotests - remove outdated insignificant Several tests were marked as 'insignificant' in the past, but nowadays tests can deal with VMs without BT adapters. Change-Id: If657b3723a9a3f88c9c638105a077fd7396931d8 Reviewed-by: Alex Blasche --- tests/auto/qbluetoothserver/qbluetoothserver.pro | 1 - tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro | 1 - tests/auto/qlowenergycontroller/qlowenergycontroller.pro | 1 - tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro | 2 -- 4 files changed, 5 deletions(-) diff --git a/tests/auto/qbluetoothserver/qbluetoothserver.pro b/tests/auto/qbluetoothserver/qbluetoothserver.pro index 870a28d7..ad0d1fab 100644 --- a/tests/auto/qbluetoothserver/qbluetoothserver.pro +++ b/tests/auto/qbluetoothserver/qbluetoothserver.pro @@ -4,7 +4,6 @@ CONFIG += testcase QT = core concurrent bluetooth testlib osx:QT += widgets -osx:CONFIG += insignificant_test OTHER_FILES += \ README.txt diff --git a/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro b/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro index a9a31acb..69bcef42 100644 --- a/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro +++ b/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro @@ -5,5 +5,4 @@ CONFIG += testcase QT = core bluetooth testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 -osx:CONFIG += insignificant_test diff --git a/tests/auto/qlowenergycontroller/qlowenergycontroller.pro b/tests/auto/qlowenergycontroller/qlowenergycontroller.pro index 9d25d8a3..24178b6e 100644 --- a/tests/auto/qlowenergycontroller/qlowenergycontroller.pro +++ b/tests/auto/qlowenergycontroller/qlowenergycontroller.pro @@ -10,5 +10,4 @@ SOURCES += tst_qlowenergycontroller.cpp osx|ios { QT += widgets - CONFIG += insignificant_test } diff --git a/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro b/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro index 4df897b8..81ec9566 100644 --- a/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro +++ b/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro @@ -4,5 +4,3 @@ CONFIG += testcase QT = core bluetooth testlib -osx:CONFIG += insignificant_test - -- cgit v1.2.3 From 2d9c73c35b99453f8f36f9dcc4a4f6a8856d7877 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Fri, 27 Nov 2015 09:17:57 +0100 Subject: Enforce unbuffered L2CAP socket for GATT The GATT protocol does not enforce a length indicator on each GATT command. Usually this is not a problem because each request is followed by a response and therefore we can assume the entire packet content is a single response/request. However the GATT WRITE_COMMAND does not have a response. Multiple requests can be sent in quick follow-up. If buffering is utilized the consecutive commands may be merged and received as single data blob on the remote device. Since Write commands don't have a length indicator they cannot be distinguished and ultimately the write command will set the wrong value. Change-Id: I02f3cd3cfaedfeae6a40272f5d22d3d88c39aa55 Task-number: QTBUG-49650 Reviewed-by: Christian Kandeler --- src/bluetooth/qlowenergycontroller_bluez.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp index 8906eb06..4cd3afe3 100644 --- a/src/bluetooth/qlowenergycontroller_bluez.cpp +++ b/src/bluetooth/qlowenergycontroller_bluez.cpp @@ -262,7 +262,9 @@ void QLowEnergyControllerPrivate::connectToDevice() } // connect - l2cpSocket->connectToService(remoteDevice, ATTRIBUTE_CHANNEL_ID); + // Unbuffered mode required to separate each GATT packet + l2cpSocket->connectToService(remoteDevice, ATTRIBUTE_CHANNEL_ID, + QIODevice::ReadWrite | QIODevice::Unbuffered); } void QLowEnergyControllerPrivate::l2cpConnected() -- cgit v1.2.3 From e117459e0d3d0670aa2cc60dfa3eafe81bbf11a9 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 24 Nov 2015 10:28:01 +0100 Subject: Bluetooth LE scan - move to its own dispatch queue (iOS/OS X) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move CBCentralManager's delegate to its own dispatch queue, not the main one, so that even if somebody _blocks_ the main thread somehow, waiting for discovery to finish, the discovery can, indeed, finish. Also, make DDA aware of the fact that now actual scan and 'this' are working on different threads, thus we must be thread-safe. Task-number: QTBUG-49476 Change-Id: I9bcc74131f51389c8a014577c65e0943bbc8da42 Reviewed-by: Morten Johan Sørvig Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 3 +- src/bluetooth/osx/osxbtledeviceinquiry.mm | 325 ++++++++++----------- src/bluetooth/osx/osxbtledeviceinquiry_p.h | 73 ++--- src/bluetooth/osx/osxbtutility.mm | 41 +++ src/bluetooth/osx/osxbtutility_p.h | 4 + .../qbluetoothdevicediscoveryagent_ios.mm | 231 +++++++++------ .../qbluetoothdevicediscoveryagent_osx.mm | 168 +++++------ 7 files changed, 452 insertions(+), 393 deletions(-) diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index e37ad7aa..6cf0795c 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -164,7 +164,8 @@ config_bluez:qtHaveModule(dbus) { qlowenergyservice_osx.mm PRIVATE_HEADERS += \ - qlowenergycontroller_osx_p.h + qlowenergycontroller_osx_p.h \ + qbluetoothdevicediscoverytimer_osx_p.h include(osx/osxbt.pri) SOURCES += \ diff --git a/src/bluetooth/osx/osxbtledeviceinquiry.mm b/src/bluetooth/osx/osxbtledeviceinquiry.mm index 28bfd1bc..f3a95820 100644 --- a/src/bluetooth/osx/osxbtledeviceinquiry.mm +++ b/src/bluetooth/osx/osxbtledeviceinquiry.mm @@ -46,10 +46,6 @@ QT_BEGIN_NAMESPACE namespace OSXBluetooth { -LEDeviceInquiryDelegate::~LEDeviceInquiryDelegate() -{ -} - #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_6_0) QBluetoothUuid qt_uuid(NSUUID *nsUuid) @@ -107,32 +103,19 @@ using namespace QT_NAMESPACE; #endif -@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) (PrivateAPI) -// "Timeout" callback to stop a scan. +@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) (PrivateAPI) - (void)stopScan; -- (void)handlePoweredOffAfterDelay; +- (void)handlePoweredOff; @end @implementation QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) -+ (int)inquiryLength -{ - // There is no default timeout, - // scan does not stop if not asked. - // Return in milliseconds - return 10 * 1000; -} - -- (id)initWithDelegate:(OSXBluetooth::LEDeviceInquiryDelegate *)aDelegate +- (id)init { - Q_ASSERT_X(aDelegate, Q_FUNC_INFO, "invalid delegate (null)"); - if (self = [super init]) { - delegate = aDelegate; - peripherals = [[NSMutableDictionary alloc] init]; - manager = nil; - scanPhase = noActivity; - cancelled = false; + uuids.reset([[NSMutableSet alloc] init]); + internalState = InquiryStarting; + state.store(int(internalState)); } return self; @@ -140,150 +123,137 @@ using namespace QT_NAMESPACE; - (void)dealloc { - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - if (manager) { [manager setDelegate:nil]; - if (scanPhase == activeScan) + if (internalState == InquiryActive) [manager stopScan]; - [manager release]; } - [peripherals release]; [super dealloc]; } - (void)stopScan { - // Scan's timeout. - Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); - Q_ASSERT_X(manager, Q_FUNC_INFO, "invalid central (nil)"); - Q_ASSERT_X(scanPhase == activeScan, Q_FUNC_INFO, "invalid state"); - Q_ASSERT_X(!cancelled, Q_FUNC_INFO, "invalid state"); - - [manager setDelegate:nil]; - [manager stopScan]; - scanPhase = noActivity; - - delegate->LEdeviceInquiryFinished(); -} + // Scan's "timeout" - we consider LE device + // discovery finished. + using namespace OSXBluetooth; -- (void)handlePoweredOffAfterDelay -{ - // If we are here, this means: - // we received 'PoweredOff' while scanPhase == startingScan - // and no 'PoweredOn' after this. - - Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); - Q_ASSERT_X(scanPhase == startingScan, Q_FUNC_INFO, "invalid state"); - - scanPhase = noActivity; - if (cancelled) { - // Timeout happened before - // the second status update, but after 'stop'. - delegate->LEdeviceInquiryFinished(); - } else { - // Timeout and no 'stop' between 'start' - // and 'centralManagerDidUpdateStatus': - delegate->LEnotSupported(); + if (internalState == InquiryActive) { + if (scanTimer.elapsed() >= qt_LE_deviceInquiryLength() * 1000) { + // We indeed stop now: + [manager stopScan]; + [manager setDelegate:nil]; + internalState = InquiryFinished; + state.store(int(internalState)); + } else { + dispatch_queue_t leQueue(qt_LE_queue()); + Q_ASSERT(leQueue); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, + int64_t(qt_LE_deviceInquiryLength() / 100. * NSEC_PER_SEC)), + leQueue, + ^{ + [self stopScan]; + }); + } } } -- (bool)start +- (void)handlePoweredOff { - Q_ASSERT_X(![self isActive], Q_FUNC_INFO, "LE device scan is already active"); - Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); - - if (!peripherals) { - qCCritical(QT_BT_OSX) << Q_FUNC_INFO << "internal error"; - return false; - } - - cancelled = false; - [peripherals removeAllObjects]; + // This is interesting on iOS only, where + // the system shows an alert asking to enable + // Bluetooth in the 'Settings' app. If not done yet (after 30 + // seconds) - we consider it an error. + if (internalState == InquiryStarting) { + if (errorTimer.elapsed() >= 30000) { + [manager setDelegate:nil]; + internalState = ErrorPoweredOff; + state.store(int(internalState)); + } else { + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT(leQueue); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, + (int64_t)(30 / 100. * NSEC_PER_SEC)), + leQueue, + ^{ + [self handlePoweredOff]; + }); - if (manager) { - // We can never be here, if status was not updated yet. - [manager setDelegate:nil]; - [manager release]; + } } +} - startTime = QTime(); - scanPhase = startingScan; - manager = [CBCentralManager alloc]; - manager = [manager initWithDelegate:self queue:nil]; - if (!manager) { - qCCritical(QT_BT_OSX) << Q_FUNC_INFO << "failed to create a central manager"; - return false; - } +- (void)start +{ + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - return true; + Q_ASSERT(leQueue); + manager.reset([[CBCentralManager alloc] initWithDelegate:self queue:leQueue]); } - (void)centralManagerDidUpdateState:(CBCentralManager *)central { - Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); - - const CBCentralManagerState state = central.state; - - if (scanPhase == startingScan && (state == CBCentralManagerStatePoweredOn - || state == CBCentralManagerStateUnsupported - || state == CBCentralManagerStateUnauthorized - || state == CBCentralManagerStatePoweredOff)) { - // We probably had 'PoweredOff' before, - // cancel the previous handlePoweredOffAfterDelay. - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - } + if (central != manager) + return; - if (cancelled) { - Q_ASSERT_X(scanPhase != activeScan, Q_FUNC_INFO, "in 'activeScan' phase"); - scanPhase = noActivity; - delegate->LEdeviceInquiryFinished(); + if (internalState != InquiryActive && internalState != InquiryStarting) return; - } - if (state == CBCentralManagerStatePoweredOn) { - if (scanPhase == startingScan) { - scanPhase = activeScan; -#ifndef Q_OS_OSX - const NSTimeInterval timeout([QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) inquiryLength] / 1000); - Q_ASSERT_X(timeout > 0., Q_FUNC_INFO, "invalid scan timeout"); - [self performSelector:@selector(stopScan) withObject:nil afterDelay:timeout]; -#endif - startTime = QTime::currentTime(); + using namespace OSXBluetooth; + + dispatch_queue_t leQueue(qt_LE_queue()); + Q_ASSERT(leQueue); + + const CBCentralManagerState cbState(central.state); + if (cbState == CBCentralManagerStatePoweredOn) { + if (internalState == InquiryStarting) { + internalState = InquiryActive; + // Scan time is actually 10 seconds. Having a block with such delay can prevent + // 'self' from being deleted in time, which is not good. So we split this + // 10 s. timeout into smaller 'chunks'. + scanTimer.start(); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, + int64_t(qt_LE_deviceInquiryLength() / 100. * NSEC_PER_SEC)), + leQueue, + ^{ + [self stopScan]; + }); [manager scanForPeripheralsWithServices:nil options:nil]; } // Else we ignore. } else if (state == CBCentralManagerStateUnsupported || state == CBCentralManagerStateUnauthorized) { - if (scanPhase == startingScan) { - scanPhase = noActivity; - delegate->LEnotSupported(); - } else if (scanPhase == activeScan) { - // Cancel stopScan: - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - scanPhase = noActivity; + if (internalState == InquiryActive) { [manager stopScan]; - delegate->LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); + // Not sure how this is possible at all, probably, can never happen. + internalState = ErrorPoweredOff; + } else { + internalState = ErrorLENotSupported; } - } else if (state == CBCentralManagerStatePoweredOff) { - if (scanPhase == startingScan) { + + [manager setDelegate:nil]; + } else if (cbState == CBCentralManagerStatePoweredOff) { + if (internalState == InquiryStarting) { #ifndef Q_OS_OSX // On iOS a user can see at this point an alert asking to enable // Bluetooth in the "Settings" app. If a user does, // we'll receive 'PoweredOn' state update later. - [self performSelector:@selector(handlePoweredOffAfterDelay) withObject:nil afterDelay:30.]; + // No change in state. Wait for 30 seconds (we split it into 'chunks' not + // to retain 'self' for too long ) ... + errorTimer.start(); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, + (int64_t)(30 / 100. * NSEC_PER_SEC)), + leQueue, + ^{ + [self handlePoweredOff]; + }); return; #endif - scanPhase = noActivity; - delegate->LEnotSupported(); - } else if (scanPhase == activeScan) { - // Cancel stopScan: - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - scanPhase = noActivity; + internalState = ErrorPoweredOff; + } else { + internalState = ErrorPoweredOff; [manager stopScan]; - delegate->LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); - } // Else we ignore. + } + + [manager setDelegate:nil]; } else { // The following two states we ignore (from Apple's docs): //" @@ -294,44 +264,36 @@ using namespace QT_NAMESPACE; // -CBCentralManagerStateResetting // The connection with the system service was momentarily // lost; an update is imminent. " + // Wait for this imminent update. } + + state.store(int(internalState)); } - (void)stop { - if (scanPhase != startingScan) { - // startingScan means either no selector at all, - // or handlePoweredOffAfterDelay and we do not want to cancel it yet, - // waiting for DidUpdateState or handlePoweredOffAfterDelay, whoever - // fires first ... - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - } - - if (scanPhase == startingScan || cancelled) { - // We have to wait for a status update or handlePoweredOffAfterDelay. - cancelled = true; - return; - } - - if (scanPhase == activeScan) { + if (internalState == InquiryActive) [manager stopScan]; - scanPhase = noActivity; - delegate->LEdeviceInquiryFinished(); - } + + [manager setDelegate:nil]; + internalState = InquiryCancelled; + state.store(int(internalState)); } - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { - Q_UNUSED(central) - Q_UNUSED(advertisementData) + Q_UNUSED(advertisementData); using namespace OSXBluetooth; - if (scanPhase != activeScan) + if (central != manager) return; - Q_ASSERT_X(delegate, Q_FUNC_INFO, "invalid delegate (null)"); + if (internalState != InquiryActive) + return; + + QBluetoothUuid deviceUuid; #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_7_0) if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0)) { @@ -340,45 +302,64 @@ using namespace QT_NAMESPACE; 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 ([uuids containsObject:peripheral.identifier]) { + // We already know this peripheral ... + return; } - return; + + [uuids addObject:peripheral.identifier]; + deviceUuid = OSXBluetooth::qt_uuid(peripheral.identifier); } #endif // Either SDK or the target is below 10.9/7.0: // The property UUID was finally removed in iOS 9, we have // to avoid compilation errors ... - CFUUIDRef cfUUID = Q_NULLPTR; + if (deviceUuid.isNull()) { + CFUUIDRef cfUUID = Q_NULLPTR; + + if ([peripheral respondsToSelector:@selector(UUID)]) { + // This will require a bridged cast if we switch to ARC ... + cfUUID = reinterpret_cast([peripheral performSelector:@selector(UUID)]); + } - if ([peripheral respondsToSelector:@selector(UUID)]) { - // This will require a bridged cast if we switch to ARC ... - cfUUID = reinterpret_cast([peripheral performSelector:@selector(UUID)]); + if (!cfUUID) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "peripheral without CFUUID"; + return; + } + + StringStrongReference key(uuid_as_nsstring(cfUUID)); + if ([uuids containsObject:key.data()]) + return; // We've seen this peripheral before ... + [uuids addObject:key.data()]; + deviceUuid = OSXBluetooth::qt_uuid(cfUUID); } - if (!cfUUID) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "peripheral without CFUUID"; + if (deviceUuid.isNull()) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no way to address peripheral, QBluetoothUuid is null"; return; } - StringStrongReference key(uuid_as_nsstring(cfUUID)); - if (![peripherals objectForKey:key.data()]) { - [peripherals setObject:peripheral forKey:key.data()]; - const QBluetoothUuid deviceUuid(OSXBluetooth::qt_uuid(cfUUID)); - delegate->LEdeviceFound(peripheral, deviceUuid, advertisementData, RSSI); - } + QString name; + if (peripheral.name) + name = QString::fromNSString(peripheral.name); + + // TODO: fix 'classOfDevice' (0 for now). + QBluetoothDeviceInfo newDeviceInfo(deviceUuid, name, 0); + if (RSSI) + newDeviceInfo.setRssi([RSSI shortValue]); + // CoreBluetooth scans only for LE devices. + newDeviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); + devices.append(newDeviceInfo); } -- (bool)isActive +- (LEInquiryState) inquiryState { - return scanPhase == startingScan || scanPhase == activeScan; + return LEInquiryState(state.load()); } -- (const QTime&)startTime +- (const QList &)discoveredDevices { - return startTime; + return devices; } @end diff --git a/src/bluetooth/osx/osxbtledeviceinquiry_p.h b/src/bluetooth/osx/osxbtledeviceinquiry_p.h index cb86cd14..9ca299ea 100644 --- a/src/bluetooth/osx/osxbtledeviceinquiry_p.h +++ b/src/bluetooth/osx/osxbtledeviceinquiry_p.h @@ -46,80 +46,59 @@ // #include "qbluetoothdevicediscoveryagent.h" +#include "qbluetoothdeviceinfo.h" +#include "osxbtutility_p.h" -#include +#include #include +#include #include #include -@class QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry); - @class CBCentralManager; @class CBPeripheral; QT_BEGIN_NAMESPACE -class QBluetoothDeviceInfo; class QBluetoothUuid; -namespace OSXBluetooth { - -class LEDeviceInquiryDelegate -{ -public: - typedef QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) LEDeviceInquiryObjC; - - virtual ~LEDeviceInquiryDelegate(); - - // At the moment the only error we're reporting is PoweredOffError! - virtual void LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) = 0; - - virtual void LEnotSupported() = 0; - virtual void LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &uuid, - NSDictionary *advertisementData, NSNumber *RSSI) = 0; - virtual void LEdeviceInquiryFinished() = 0; -}; - -} - QT_END_NAMESPACE -// Bluetooth Low Energy scan for iOS and OS X. -// Strong enum would be quite handy ... -enum LEScanPhase +enum LEInquiryState { - noActivity, - startingScan, - activeScan + InquiryStarting, + InquiryActive, + InquiryFinished, + InquiryCancelled, + ErrorPoweredOff, + ErrorLENotSupported }; @interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) : NSObject -{// Protocols are adopted in the mm file. - QT_PREPEND_NAMESPACE(OSXBluetooth)::LEDeviceInquiryDelegate *delegate; +{ + QT_PREPEND_NAMESPACE(OSXBluetooth)::ObjCScopedPointer uuids; + QT_PREPEND_NAMESPACE(OSXBluetooth)::ObjCScopedPointer manager; - // TODO: scoped pointers/shared pointers? - NSMutableDictionary *peripherals; // Found devices. - CBCentralManager *manager; + QList devices; - LEScanPhase scanPhase; - bool cancelled; - QTime startTime; -} + LEInquiryState internalState; + QT_PREPEND_NAMESPACE(QAtomicInt) state; -// Inquiry length in milliseconds. -+ (int)inquiryLength; + // Timers to check if we can execute delayed callbacks: + QT_PREPEND_NAMESPACE(QElapsedTimer) errorTimer; + QT_PREPEND_NAMESPACE(QElapsedTimer) scanTimer; +} -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth)::LEDeviceInquiryDelegate *)aDelegate; +- (id)init; - (void)dealloc; -// Actual scan can be delayed - we have to wait for a status update first. -- (bool)start; -// Stop can be delayed - if we're waiting for a status update. +// IMPORTANT: both 'start' and 'stop' are to be executed on the "Qt's LE queue". +- (void)start; - (void)stop; -- (bool)isActive; -- (const QTime &)startTime; +- (LEInquiryState)inquiryState; +- (const QList &)discoveredDevices; @end diff --git a/src/bluetooth/osx/osxbtutility.mm b/src/bluetooth/osx/osxbtutility.mm index a5d3d936..08ff699d 100644 --- a/src/bluetooth/osx/osxbtutility.mm +++ b/src/bluetooth/osx/osxbtutility.mm @@ -305,6 +305,47 @@ ObjCStrongReference data_from_bytearray(const QByteArray & qtData) return result; } +// A small RAII class for a dispatch queue. +class SerialDispatchQueue +{ +public: + explicit SerialDispatchQueue(const char *label) + { + Q_ASSERT(label); + + queue = dispatch_queue_create(label, DISPATCH_QUEUE_SERIAL); + if (!queue) { + qCCritical(QT_BT_OSX) << "failed to create dispatch queue with label" + << label; + } + } + ~SerialDispatchQueue() + { + if (queue) + dispatch_release(queue); + } + + dispatch_queue_t data() const + { + return queue; + } +private: + dispatch_queue_t queue; + + Q_DISABLE_COPY(SerialDispatchQueue) +}; + +dispatch_queue_t qt_LE_queue() +{ + static const SerialDispatchQueue leQueue("qt-bluetooth-LE-queue"); + return leQueue.data(); +} + +unsigned qt_LE_deviceInquiryLength() +{ + return 10; +} + } QT_END_NAMESPACE diff --git a/src/bluetooth/osx/osxbtutility_p.h b/src/bluetooth/osx/osxbtutility_p.h index a69e05c2..3506b0d1 100644 --- a/src/bluetooth/osx/osxbtutility_p.h +++ b/src/bluetooth/osx/osxbtutility_p.h @@ -306,6 +306,10 @@ inline QSysInfo::MacVersion qt_OS_limit(QSysInfo::MacVersion osxVersion, QSysInf #endif } +dispatch_queue_t qt_LE_queue(); +// LE scan, in seconds. +unsigned qt_LE_deviceInquiryLength(); + } // namespace OSXBluetooth // Logging category for both OS X and iOS. diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm index 1556c5f9..30a6acb6 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm @@ -31,6 +31,7 @@ ** ****************************************************************************/ +#include "qbluetoothdevicediscoverytimer_osx_p.h" #include "qbluetoothdevicediscoveryagent.h" #include "osx/osxbtledeviceinquiry_p.h" #include "qbluetoothlocaldevice.h" @@ -51,27 +52,29 @@ QT_BEGIN_NAMESPACE using OSXBluetooth::ObjCScopedPointer; -class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::LEDeviceInquiryDelegate +class QBluetoothDeviceDiscoveryAgentPrivate { friend class QBluetoothDeviceDiscoveryAgent; + friend class OSXBluetooth::DDATimerHandler; + public: QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &address, QBluetoothDeviceDiscoveryAgent *q); virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); - bool isValid() const; bool isActive() const; void start(); void stop(); private: - // LEDeviceInquiryDelegate: - void LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) Q_DECL_OVERRIDE; - void LEnotSupported() Q_DECL_OVERRIDE; - void LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &deviceUuid, - NSDictionary *advertisementData, NSNumber *RSSI) Q_DECL_OVERRIDE; - void LEdeviceInquiryFinished() Q_DECL_OVERRIDE; + typedef QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) LEDeviceInquiryObjC; + + void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); + void LEnotSupported(); + void LEdeviceFound(const QBluetoothDeviceInfo &info); + void LEinquiryFinished(); + void checkLETimeout(); void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString()); @@ -90,8 +93,45 @@ private: bool startPending; bool stopPending; + + QScopedPointer timer; }; +namespace OSXBluetooth { + +DDATimerHandler::DDATimerHandler(QBluetoothDeviceDiscoveryAgentPrivate *d) + : owner(d) +{ + Q_ASSERT_X(owner, Q_FUNC_INFO, "invalid pointer"); + + timer.setSingleShot(false); + connect(&timer, &QTimer::timeout, this, &DDATimerHandler::onTimer); +} + +void DDATimerHandler::start(int msec) +{ + Q_ASSERT_X(msec > 0, Q_FUNC_INFO, "invalid time interval"); + if (timer.isActive()) { + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "timer is active"; + return; + } + + timer.start(msec); +} + +void DDATimerHandler::stop() +{ + timer.stop(); +} + +void DDATimerHandler::onTimer() +{ + Q_ASSERT(owner); + owner->checkLETimeout(); +} + +} + QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter, QBluetoothDeviceDiscoveryAgent *q) : q_ptr(q), @@ -103,29 +143,20 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con Q_UNUSED(adapter); Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)"); - - // OSXBTLEDeviceInquiry can be constructed even if LE is not supported - - // at this stage it's only a memory allocation of the object itself, - // if it fails - we have some memory-related problems. - LEDeviceInquiry newInquiryLE([[LEDeviceInquiryObjC alloc] initWithDelegate:this]); - if (!newInquiryLE) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to initialize a device inquiry object"; - return; - } - - inquiryLE.reset(newInquiryLE.take()); } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const -{ - // isValid() - Qt does not use exceptions, but the ctor - // can fail to initialize some important data-members - // - this is what meant here by valid/invalid. - return inquiryLE; + if (inquiryLE) { + // We want the LE scan to stop as soon as possible. + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + // Local variable to be retained ... + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq stop]; + }); + } + } } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const @@ -135,50 +166,66 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const if (stopPending) return false; - return [inquiryLE isActive]; + return inquiryLE; } void QBluetoothDeviceDiscoveryAgentPrivate::start() { - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "called on invalid device discovery agent"); Q_ASSERT_X(!isActive(), Q_FUNC_INFO, "called on active device discovery agent"); - Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, - Q_FUNC_INFO, "called with an invalid Bluetooth adapter"); if (stopPending) { startPending = true; return; } - discoveredDevices.clear(); - setError(QBluetoothDeviceDiscoveryAgent::NoError); + using namespace OSXBluetooth; - if (![inquiryLE start]) { - // We can be here only if we have some kind of - // resource allocation error. + inquiryLE.reset([[LEDeviceInquiryObjC alloc] init]); + dispatch_queue_t leQueue(qt_LE_queue()); + if (!leQueue || !inquiryLE) { setError(QBluetoothDeviceDiscoveryAgent::UnknownError, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED)); emit q_ptr->error(lastError); } + + discoveredDevices.clear(); + setError(QBluetoothDeviceDiscoveryAgent::NoError); + + // CoreBluetooth does not have a timeout. We start a timer here + // and check if scan really started and if yes if we have a timeout. + timer.reset(new OSXBluetooth::DDATimerHandler(this)); + timer->start(2000); + + // Create a local variable - to have a strong referece in a block. + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq start]; + }); } void QBluetoothDeviceDiscoveryAgentPrivate::stop() { - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "called on invalid device discovery agent"); Q_ASSERT_X(isActive(), Q_FUNC_INFO, "called whithout active inquiry"); - Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, - Q_FUNC_INFO, "called with invalid bluetooth adapter"); startPending = false; stopPending = true; + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT(leQueue); + setError(QBluetoothDeviceDiscoveryAgent::NoError); - // Can be asynchronous (depending on a status update of CBCentralManager). - // The call itself is always 'success'. - [inquiryLE stop]; + + // Create a local variable - to have a strong referece in a block. + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq stop]; + }); + // We consider LE scan to be stopped immediately and + // do not care about this LEDeviceInquiry object anymore. + LEinquiryFinished(); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error) { // At the moment the only error reported by osxbtledeviceinquiry // can be 'powered off' error, it happens @@ -187,6 +234,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDevic Q_ASSERT_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError, Q_FUNC_INFO, "unexpected error"); + inquiryLE.reset(); + timer->stop(); + startPending = false; stopPending = false; setError(error); @@ -195,36 +245,17 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDevic void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() { + inquiryLE.reset(); + timer->stop(); + startPending = false; stopPending = false; setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError); emit q_ptr->error(lastError); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &deviceUuid, - NSDictionary *advertisementData, - NSNumber *RSSI) +void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(const QBluetoothDeviceInfo &newDeviceInfo) { - Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); - - QT_BT_MAC_AUTORELEASEPOOL; - - QString name; - if (peripheral.name && peripheral.name.length) { - name = QString::fromNSString(peripheral.name); - } else { - NSString *const localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey]; - if (localName && [localName length]) - name = QString::fromNSString(localName); - } - - // TODO: fix 'classOfDevice' (0 for now). - QBluetoothDeviceInfo newDeviceInfo(deviceUuid, name, 0); - if (RSSI) - newDeviceInfo.setRssi([RSSI shortValue]); - // CoreBluetooth scans only for LE devices. - newDeviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); - // Update, append or discard. for (int i = 0, e = discoveredDevices.size(); i < e; ++i) { if (discoveredDevices[i].deviceUuid() == newDeviceInfo.deviceUuid()) { @@ -241,9 +272,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(CBPeripheral *peripher emit q_ptr->deviceDiscovered(newDeviceInfo); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryFinished() +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() { - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); + inquiryLE.reset(); + timer->stop(); if (stopPending && !startPending) { stopPending = false; @@ -257,6 +289,41 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryFinished() } } +void QBluetoothDeviceDiscoveryAgentPrivate::checkLETimeout() +{ + Q_ASSERT_X(inquiryLE, Q_FUNC_INFO, "LE device inquiry is nil"); + + using namespace OSXBluetooth; + + const LEInquiryState state([inquiryLE inquiryState]); + if (state == InquiryStarting || state == InquiryActive) + return; // Wait ... + + if (state == ErrorPoweredOff) + return LEinquiryError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); + + if (state == ErrorLENotSupported) + return LEnotSupported(); + + if (state == InquiryFinished) { + // Process found devices if any ... + const QList leDevices([inquiryLE discoveredDevices]); + foreach (const QBluetoothDeviceInfo &info, leDevices) { + // We were cancelled on a previous device discovered signal ... + if (!inquiryLE) + break; + LEdeviceFound(info); + } + + if (inquiryLE) + LEinquiryFinished(); + return; + } + + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unexpected inquiry state in LE timeout"; + // Actually, this deserves an assert :) +} + void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAgent::Error error, const QString &text) { @@ -329,36 +396,22 @@ QList QBluetoothDeviceDiscoveryAgent::discoveredDevices() void QBluetoothDeviceDiscoveryAgent::start() { if (d_ptr->lastError != InvalidBluetoothAdapterError) { - if (d_ptr->isValid()) { - if (!isActive()) - d_ptr->start(); - else - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already started"; - } else { - // We previously failed to initialize - // private object correctly. - d_ptr->setError(InvalidBluetoothAdapterError); - emit error(InvalidBluetoothAdapterError); - } + if (!isActive()) + d_ptr->start(); + else + qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already started"; } } void QBluetoothDeviceDiscoveryAgent::stop() { - if (d_ptr->isValid()) { - if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) - d_ptr->stop(); - else - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "failed to stop"; - } + if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) + d_ptr->stop(); } bool QBluetoothDeviceDiscoveryAgent::isActive() const { - if (d_ptr->isValid()) - return d_ptr->isActive(); - - return false; + return d_ptr->isActive(); } QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm index ad4183a7..1cfe8286 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm @@ -59,15 +59,16 @@ QT_BEGIN_NAMESPACE using OSXBluetooth::ObjCScopedPointer; -class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::DeviceInquiryDelegate, - public OSXBluetooth::LEDeviceInquiryDelegate +class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::DeviceInquiryDelegate { friend class QBluetoothDeviceDiscoveryAgent; friend class OSXBluetooth::DDATimerHandler; public: + typedef QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) LEDeviceInquiryObjC; + QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress & address, QBluetoothDeviceDiscoveryAgent *q); - virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); // Just to make compiler happy. + virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); bool isValid() const; bool isActive() const; @@ -87,12 +88,11 @@ private: void inquiryFinished(IOBluetoothDeviceInquiry *inq) Q_DECL_OVERRIDE; void error(IOBluetoothDeviceInquiry *inq, IOReturn error) Q_DECL_OVERRIDE; void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) Q_DECL_OVERRIDE; - // LEDeviceInquiryDelegate: - void LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) Q_DECL_OVERRIDE; - void LEnotSupported() Q_DECL_OVERRIDE; - void LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &deviceUuid, - NSDictionary *advertisementData, NSNumber *RSSI) Q_DECL_OVERRIDE; - void LEdeviceInquiryFinished() Q_DECL_OVERRIDE; + + // + void LEinquiryFinished(); + void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); + void LEnotSupported(); // Check if it's a really new device/updated info and emit // q_ptr->deviceDiscovered. @@ -138,7 +138,7 @@ DDATimerHandler::DDATimerHandler(QBluetoothDeviceDiscoveryAgentPrivate *d) { Q_ASSERT_X(owner, Q_FUNC_INFO, "invalid pointer"); - timer.setSingleShot(true); + timer.setSingleShot(false); connect(&timer, &QTimer::timeout, this, &DDATimerHandler::onTimer); } @@ -192,23 +192,22 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con return; } - // OSXBTLEDeviceInquiry can be constructed even if LE is not supported - - // at this stage it's only a memory allocation of the object itself, - // if it fails - we have some memory-related problem. - LEDeviceInquiry newInquiryLE([[LEDeviceInquiryObjC alloc] initWithDelegate:this]); - if (!newInquiryLE) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to " - "initialize a LE inquiry"; - return; - } - hostController.reset(controller.take()); inquiry.reset(newInquiry.take()); - inquiryLE.reset(newInquiryLE.take()); } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { + if (inquiryLE && agentState != NonActive) { + // We want the LE scan to stop as soon as possible. + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + // Local variable to be retained ... + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq stop]; + }); + } + } } bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const @@ -218,7 +217,7 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const // (and the error is probably not even related to Bluetooth at all) // - say, allocation error - this is what meant here by valid/invalid. return hostController && [hostController powerState] == kBluetoothHCIPowerStateON - && inquiry && inquiryLE; + && inquiry; } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const @@ -263,22 +262,29 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE() Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, Q_FUNC_INFO, "called with an invalid Bluetooth adapter"); - agentState = LEScan; + using namespace OSXBluetooth; - // CoreBluetooth does not have a timeout. We start a timer here - // and check if scan really started and if yes if we have a timeout. - timer.reset(new OSXBluetooth::DDATimerHandler(this)); - timer->start([LEDeviceInquiryObjC inquiryLength]); + inquiryLE.reset([[LEDeviceInquiryObjC alloc] init]); - if (![inquiryLE start]) { - // We can be here only if we have some kind of resource allocation error, so we - // do not emit finished, we emit error. - timer->stop(); + dispatch_queue_t leQueue(qt_LE_queue()); + if (!leQueue || !inquiryLE) { setError(QBluetoothDeviceDiscoveryAgent::UnknownError, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED_LE)); agentState = NonActive; emit q_ptr->error(lastError); } + + agentState = LEScan; + // CoreBluetooth does not have a timeout. We start a timer here + // and check if scan is active/finished/finished with error(s). + timer.reset(new OSXBluetooth::DDATimerHandler(this)); + timer->start(2000); + + // We need the local variable so that it's retained ... + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq start]; + }); } void QBluetoothDeviceDiscoveryAgentPrivate::stop() @@ -288,6 +294,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, Q_FUNC_INFO, "called with invalid bluetooth adapter"); + using namespace OSXBluetooth; + const bool prevStart = startPending; startPending = false; stopPending = true; @@ -304,9 +312,16 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() emit q_ptr->error(lastError); } } else { - // Can be asynchronous (depending on a status update of CBCentralManager). - // The call itself is always 'success'. - [inquiryLE stop]; + dispatch_queue_t leQueue(qt_LE_queue()); + Q_ASSERT(leQueue); + // We need the local variable so that it's retained ... + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq stop]; + }); + // We consider LE scan to be stopped immediately and + // do not care about this LEDeviceInquiry object anymore. + LEinquiryFinished(); } } @@ -430,31 +445,45 @@ void QBluetoothDeviceDiscoveryAgentPrivate::checkLETimeout() Q_ASSERT_X(agentState == LEScan, Q_FUNC_INFO, "invalid agent state"); Q_ASSERT_X(inquiryLE, Q_FUNC_INFO, "LE device inquiry is nil"); - const int timeout = [LEDeviceInquiryObjC inquiryLength]; - Q_ASSERT(timeout > 0); - const QTime scanStartTime([inquiryLE startTime]); - if (scanStartTime.isValid()) { - const int elapsed = scanStartTime.msecsTo(QTime::currentTime()); - Q_ASSERT(elapsed >= 0); - if (elapsed >= timeout) - [inquiryLE stop]; - else - timer->start(timeout - elapsed); - } else { - // Scan not started yet. Wait 5 seconds more. - timer->start(timeout / 2); + using namespace OSXBluetooth; + + const LEInquiryState state([inquiryLE inquiryState]); + if (state == InquiryStarting || state == InquiryActive) + return; // Wait ... + + if (state == ErrorPoweredOff) + return LEinquiryError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); + + if (state == ErrorLENotSupported) + return LEnotSupported(); + + if (state == InquiryFinished) { + // Process found devices if any ... + const QList leDevices([inquiryLE discoveredDevices]); + foreach (const QBluetoothDeviceInfo &info, leDevices) { + // We were cancelled on a previous device discovered signal ... + if (agentState != LEScan) + break; + deviceFound(info); + } + + if (agentState == LEScan) + LEinquiryFinished(); + return; } + + qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unexpected inquiry state in LE timeout"; + // Actually, this deserves an assert :) } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error) { // At the moment the only error reported can be 'powered off' error, it happens // after the LE scan started (so we have LE support and this is a real PoweredOffError). - Q_ASSERT_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError, - Q_FUNC_INFO, "unexpected error code"); + Q_ASSERT(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError); timer->stop(); - + inquiryLE.reset(); agentState = NonActive; setError(error); emit q_ptr->error(lastError); @@ -462,45 +491,16 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDevic void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() { - // Not supported is not an error. + // Not supported is not an error (we still have 'Classic'). qCDebug(QT_BT_OSX) << "no Bluetooth LE support"; - // After we call startLE and before receive NotSupported, - // the user can call stop (setting a pending stop). - // So the same rule apply: - timer->stop(); - - LEdeviceInquiryFinished(); + LEinquiryFinished(); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &deviceUuid, - NSDictionary *advertisementData, - NSNumber *RSSI) -{ - Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); - Q_ASSERT_X(agentState == LEScan, Q_FUNC_INFO, - "invalid agent state, expected LE scan"); - - Q_UNUSED(advertisementData) - - QString name; - if (peripheral.name) - name = QString::fromNSString(peripheral.name); - - // TODO: fix 'classOfDevice' (0 for now). - QBluetoothDeviceInfo newDeviceInfo(deviceUuid, name, 0); - if (RSSI) - newDeviceInfo.setRssi([RSSI shortValue]); - // CoreBluetooth scans only for LE devices. - newDeviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); - - deviceFound(newDeviceInfo); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryFinished() +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() { // The same logic as in inquiryFinished, but does not start LE scan. agentState = NonActive; - + inquiryLE.reset(); timer->stop(); if (stopPending && !startPending) { -- cgit v1.2.3 From 741f5e26cd618aeff349dd612516148b50c85dde Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Fri, 27 Nov 2015 10:19:02 +0100 Subject: Don't register meta types during static init time The QMetaType register my not be up and running by the time we attempt these meta type registration. Change-Id: I1a857a936a24b4b00a49574fac311c08c09b3d5a Task-number: QTBUG-49455 Reviewed-by: Timur Pocheptsov Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothaddress.cpp | 15 ++++++++------ src/bluetooth/qbluetoothlocaldevice.cpp | 11 ++++------ src/bluetooth/qbluetoothlocaldevice_android.cpp | 2 ++ src/bluetooth/qbluetoothlocaldevice_bluez.cpp | 2 ++ src/bluetooth/qbluetoothlocaldevice_osx.mm | 2 ++ src/bluetooth/qbluetoothlocaldevice_p.cpp | 4 ++++ src/bluetooth/qbluetoothlocaldevice_p.h | 2 ++ src/bluetooth/qbluetoothlocaldevice_qnx.cpp | 2 ++ src/bluetooth/qbluetoothuuid.cpp | 27 ++++++++++++++++--------- src/bluetooth/qlowenergycontroller.cpp | 11 +++++----- src/bluetooth/qlowenergycontroller_android.cpp | 1 + src/bluetooth/qlowenergycontroller_bluez.cpp | 1 + src/bluetooth/qlowenergycontroller_osx.mm | 16 ++++++++------- src/bluetooth/qlowenergycontroller_p.cpp | 1 + src/bluetooth/qlowenergycontroller_p.h | 2 ++ 15 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/bluetooth/qbluetoothaddress.cpp b/src/bluetooth/qbluetoothaddress.cpp index 8e3c29c6..5a0abf45 100644 --- a/src/bluetooth/qbluetoothaddress.cpp +++ b/src/bluetooth/qbluetoothaddress.cpp @@ -59,15 +59,13 @@ QT_BEGIN_NAMESPACE Returns true if the Bluetooth addresses are not equal, otherwise returns false. */ -namespace { -class BluetoothAddressRegisterMetaTypes +static void registerQBluetoothAddressMetaType() { -public: - BluetoothAddressRegisterMetaTypes() - { + static bool initDone = false; + if (!initDone) { qRegisterMetaType(); + initDone = true; } -} _registerBluetoothAddressMetaTypes; } /*! @@ -76,6 +74,7 @@ public: QBluetoothAddress::QBluetoothAddress() : d_ptr(new QBluetoothAddressPrivate) { + registerQBluetoothAddressMetaType(); } /*! @@ -84,6 +83,8 @@ QBluetoothAddress::QBluetoothAddress() : QBluetoothAddress::QBluetoothAddress(quint64 address) : d_ptr(new QBluetoothAddressPrivate) { + registerQBluetoothAddressMetaType(); + Q_D(QBluetoothAddress); d->m_address = address; } @@ -97,6 +98,8 @@ QBluetoothAddress::QBluetoothAddress(quint64 address) : QBluetoothAddress::QBluetoothAddress(const QString &address) : d_ptr(new QBluetoothAddressPrivate) { + registerQBluetoothAddressMetaType(); + Q_D(QBluetoothAddress); QString a = address; diff --git a/src/bluetooth/qbluetoothlocaldevice.cpp b/src/bluetooth/qbluetoothlocaldevice.cpp index 0eb97647..ddbce02b 100644 --- a/src/bluetooth/qbluetoothlocaldevice.cpp +++ b/src/bluetooth/qbluetoothlocaldevice.cpp @@ -97,20 +97,17 @@ QT_BEGIN_NAMESPACE */ -namespace { -class LocalDeviceRegisterMetaTypes +void registerQBluetoothLocalDeviceMetaType() { -public: - LocalDeviceRegisterMetaTypes() - { + static bool initDone = false; + if (!initDone) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + initDone = true; } -} _registerLocalDeviceMetaTypes; } - #ifndef QT_OSX_BLUETOOTH /*! diff --git a/src/bluetooth/qbluetoothlocaldevice_android.cpp b/src/bluetooth/qbluetoothlocaldevice_android.cpp index 20ac3d0a..083bc190 100644 --- a/src/bluetooth/qbluetoothlocaldevice_android.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_android.cpp @@ -52,6 +52,8 @@ QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate( obj(0), pendingHostModeTransition(false) { + registerQBluetoothLocalDeviceMetaType(); + initialize(address); receiver = new LocalDeviceBroadcastReceiver(q_ptr); diff --git a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp index cae21e1b..9b550a1b 100644 --- a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp @@ -644,6 +644,8 @@ QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice msgConnection(0), q_ptr(q) { + registerQBluetoothLocalDeviceMetaType(); + if (isBluez5()) initializeAdapterBluez5(); else diff --git a/src/bluetooth/qbluetoothlocaldevice_osx.mm b/src/bluetooth/qbluetoothlocaldevice_osx.mm index 71b7b81a..b1957d09 100644 --- a/src/bluetooth/qbluetoothlocaldevice_osx.mm +++ b/src/bluetooth/qbluetoothlocaldevice_osx.mm @@ -105,6 +105,8 @@ QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice const QBluetoothAddress &address) : q_ptr(q) { + registerQBluetoothLocalDeviceMetaType(); + Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)"); QT_BT_MAC_AUTORELEASEPOOL; diff --git a/src/bluetooth/qbluetoothlocaldevice_p.cpp b/src/bluetooth/qbluetoothlocaldevice_p.cpp index 2785e84c..0b7c5c44 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_p.cpp @@ -34,18 +34,22 @@ #include "qbluetoothlocaldevice.h" #include "qbluetoothaddress.h" +#include "qbluetoothlocaldevice_p.h" + QT_BEGIN_NAMESPACE QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : QObject(parent), d_ptr(0) { + registerQBluetoothLocalDeviceMetaType(); } QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &, QObject *parent) : QObject(parent), d_ptr(0) { + registerQBluetoothLocalDeviceMetaType(); } QString QBluetoothLocalDevice::name() const diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index c47ceb9e..7bb343ac 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -83,6 +83,8 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE +extern void registerQBluetoothLocalDeviceMetaType(); + class QBluetoothAddress; #ifdef QT_ANDROID_BLUETOOTH diff --git a/src/bluetooth/qbluetoothlocaldevice_qnx.cpp b/src/bluetooth/qbluetoothlocaldevice_qnx.cpp index c36fb89a..e4ac086a 100644 --- a/src/bluetooth/qbluetoothlocaldevice_qnx.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_qnx.cpp @@ -211,6 +211,8 @@ void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q) : q_ptr(q) { + registerQBluetoothLocalDeviceMetaType(); + ppsRegisterControl(); ppsRegisterForEvent(QStringLiteral("access_changed"), this); ppsRegisterForEvent(QStringLiteral("pairing_complete"), this); diff --git a/src/bluetooth/qbluetoothuuid.cpp b/src/bluetooth/qbluetoothuuid.cpp index cfce871c..3cef44b2 100644 --- a/src/bluetooth/qbluetoothuuid.cpp +++ b/src/bluetooth/qbluetoothuuid.cpp @@ -497,16 +497,13 @@ Q_GLOBAL_STATIC_WITH_ARGS(QUuid, baseUuid, ("{00000000-0000-1000-8000-00805F9B34 \value UnknownDescriptorType The descriptor type is unknown. */ -namespace +static void registerQBluetoothUuidMetaType() { - class BtUuidRegisterMetaTypes - { - public: - BtUuidRegisterMetaTypes() - { - qRegisterMetaType(); - } - } _registerBtUuidMetaTypes; + static bool initDone = false; + if (!initDone) { + qRegisterMetaType(); + initDone = true; + } } /*! @@ -514,6 +511,7 @@ namespace */ QBluetoothUuid::QBluetoothUuid() { + registerQBluetoothUuidMetaType(); } /*! @@ -525,6 +523,7 @@ QBluetoothUuid::QBluetoothUuid(ProtocolUuid uuid) baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5], baseUuid()->data4[6], baseUuid()->data4[7]) { + registerQBluetoothUuidMetaType(); } /*! @@ -535,6 +534,7 @@ QBluetoothUuid::QBluetoothUuid(ServiceClassUuid uuid) baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5], baseUuid()->data4[6], baseUuid()->data4[7]) { + registerQBluetoothUuidMetaType(); } /*! @@ -546,6 +546,7 @@ QBluetoothUuid::QBluetoothUuid(CharacteristicType uuid) baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5], baseUuid()->data4[6], baseUuid()->data4[7]) { + registerQBluetoothUuidMetaType(); } /*! @@ -557,7 +558,7 @@ QBluetoothUuid::QBluetoothUuid(DescriptorType uuid) baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5], baseUuid()->data4[6], baseUuid()->data4[7]) { - + registerQBluetoothUuidMetaType(); } /*! @@ -568,6 +569,7 @@ QBluetoothUuid::QBluetoothUuid(quint16 uuid) baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5], baseUuid()->data4[6], baseUuid()->data4[7]) { + registerQBluetoothUuidMetaType(); } /*! @@ -578,6 +580,7 @@ QBluetoothUuid::QBluetoothUuid(quint32 uuid) baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5], baseUuid()->data4[6], baseUuid()->data4[7]) { + registerQBluetoothUuidMetaType(); } /*! @@ -587,6 +590,7 @@ QBluetoothUuid::QBluetoothUuid(quint32 uuid) */ QBluetoothUuid::QBluetoothUuid(quint128 uuid) { + registerQBluetoothUuidMetaType(); QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wstrict-aliasing") data1 = qFromBigEndian(*reinterpret_cast(&uuid.data[0])); @@ -605,6 +609,7 @@ QT_WARNING_POP QBluetoothUuid::QBluetoothUuid(const QString &uuid) : QUuid(uuid) { + registerQBluetoothUuidMetaType(); } /*! @@ -613,6 +618,7 @@ QBluetoothUuid::QBluetoothUuid(const QString &uuid) QBluetoothUuid::QBluetoothUuid(const QBluetoothUuid &uuid) : QUuid(uuid) { + registerQBluetoothUuidMetaType(); } /*! @@ -621,6 +627,7 @@ QBluetoothUuid::QBluetoothUuid(const QBluetoothUuid &uuid) QBluetoothUuid::QBluetoothUuid(const QUuid &uuid) : QUuid(uuid) { + registerQBluetoothUuidMetaType(); } /*! diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp index 44b6a6da..28012de5 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -194,18 +194,17 @@ QT_BEGIN_NAMESPACE \sa discoverServices(), error() */ -namespace { -class QLowEnergyControllerMetaTypes +void registerQLowEnergyControllerMetaType() { -public: - QLowEnergyControllerMetaTypes() - { + static bool initDone = false; + if (!initDone) { qRegisterMetaType(); qRegisterMetaType(); + initDone = true; } -} qLowEnergyControllerMetaTypes; } + void QLowEnergyControllerPrivate::setError( QLowEnergyController::Error newError) { diff --git a/src/bluetooth/qlowenergycontroller_android.cpp b/src/bluetooth/qlowenergycontroller_android.cpp index f48c0e85..3c462f8f 100644 --- a/src/bluetooth/qlowenergycontroller_android.cpp +++ b/src/bluetooth/qlowenergycontroller_android.cpp @@ -45,6 +45,7 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() error(QLowEnergyController::NoError), hub(0) { + registerQLowEnergyControllerMetaType(); } QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp index 4cd3afe3..d9fea1e6 100644 --- a/src/bluetooth/qlowenergycontroller_bluez.cpp +++ b/src/bluetooth/qlowenergycontroller_bluez.cpp @@ -199,6 +199,7 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() encryptionChangePending(false), hciManager(0) { + registerQLowEnergyControllerMetaType(); qRegisterMetaType >(); hciManager = new HciManager(localAdapter, this); diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm index c4628069..4fbc5200 100644 --- a/src/bluetooth/qlowenergycontroller_osx.mm +++ b/src/bluetooth/qlowenergycontroller_osx.mm @@ -56,17 +56,15 @@ QT_BEGIN_NAMESPACE namespace { - -class QLowEnergyControllerMetaTypes +static void registerQLowEnergyControllerMetaType() { -public: - QLowEnergyControllerMetaTypes() - { + static bool initDone = false; + if (!initDone) { qRegisterMetaType(); qRegisterMetaType(); + initDone = true; } -} qLowEnergyControllerMetaTypes; - +} typedef QSharedPointer ServicePrivate; @@ -137,6 +135,8 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl controllerState(QLowEnergyController::UnconnectedState), addressType(QLowEnergyController::PublicAddress) { + registerQLowEnergyControllerMetaType(); + // This is the "wrong" constructor - no valid device UUID to connect later. Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)"); // We still create a manager, to simplify error handling later. @@ -157,6 +157,8 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl controllerState(QLowEnergyController::UnconnectedState), addressType(QLowEnergyController::PublicAddress) { + registerQLowEnergyControllerMetaType(); + Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)"); centralManager.reset([[ObjCCentralManager alloc] initWithDelegate:this]); if (!centralManager) { diff --git a/src/bluetooth/qlowenergycontroller_p.cpp b/src/bluetooth/qlowenergycontroller_p.cpp index b3c718b5..79addae2 100644 --- a/src/bluetooth/qlowenergycontroller_p.cpp +++ b/src/bluetooth/qlowenergycontroller_p.cpp @@ -40,6 +40,7 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() state(QLowEnergyController::UnconnectedState), error(QLowEnergyController::NoError) { + registerQLowEnergyControllerMetaType(); } QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 810f0ff4..defc4127 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -73,6 +73,8 @@ class HciManager; class LowEnergyNotificationHub; #endif +extern void registerQLowEnergyControllerMetaType(); + typedef QMap > ServiceDataMap; class QLowEnergyControllerPrivate : public QObject -- cgit v1.2.3 From 56a7c671c48d42ebbedd1c5a83d3ee3301015a18 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 30 Nov 2015 15:47:14 +0100 Subject: BT/BlueZ: Do not react to "Insufficient Authorization" error. Authorization in the context of ATT is purely a server-side feature that the client cannot influence, so increasing the security level will not help. The "Insufficient key size" error, however, should be handled. Change-Id: I14b24700f3ebc365215eefb78b0aa8487fcbfd4d Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_bluez.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp index 18d0f5a1..b1384fea 100644 --- a/src/bluetooth/qlowenergycontroller_bluez.cpp +++ b/src/bluetooth/qlowenergycontroller_bluez.cpp @@ -1723,9 +1723,9 @@ bool QLowEnergyControllerPrivate::increaseEncryptLevelfRequired(quint8 errorCode return false; switch (errorCode) { - case ATT_ERROR_INSUF_AUTHORIZATION: case ATT_ERROR_INSUF_ENCRYPTION: case ATT_ERROR_INSUF_AUTHENTICATION: + case ATT_ERROR_INSUF_ENCR_KEY_SIZE: if (!hciManager->isValid()) return false; if (!hciManager->monitorEvent(HciManager::EncryptChangeEvent)) -- cgit v1.2.3