From 5bdc4e8b0b1e6664b7c289cfc9c1a9d12da6e87e Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 28 May 2018 14:35:17 +0200 Subject: Reimplement scan/manager state timeouts using GCD timer And remove some essentially duplicated code. Task-number: QTBUG-68422 Change-Id: I677581ebb0998d64a0081f568479efb7e8156474 Reviewed-by: Alex Blasche --- src/bluetooth/osx/osxbtgcdtimer.mm | 1 + src/bluetooth/osx/osxbtledeviceinquiry.mm | 114 +++++++---------------------- src/bluetooth/osx/osxbtledeviceinquiry_p.h | 18 ++--- 3 files changed, 34 insertions(+), 99 deletions(-) (limited to 'src') diff --git a/src/bluetooth/osx/osxbtgcdtimer.mm b/src/bluetooth/osx/osxbtgcdtimer.mm index 0a49c25c..095f8680 100644 --- a/src/bluetooth/osx/osxbtgcdtimer.mm +++ b/src/bluetooth/osx/osxbtgcdtimer.mm @@ -105,6 +105,7 @@ - (void)cancelTimer { cancelled = true; + timeoutHandler = nil; } @end diff --git a/src/bluetooth/osx/osxbtledeviceinquiry.mm b/src/bluetooth/osx/osxbtledeviceinquiry.mm index 60222370..2cece15b 100644 --- a/src/bluetooth/osx/osxbtledeviceinquiry.mm +++ b/src/bluetooth/osx/osxbtledeviceinquiry.mm @@ -65,9 +65,7 @@ QBluetoothUuid qt_uuid(NSUUID *nsUuid) } const int timeStepMS = 100; - const int powerOffTimeoutMS = 30000; -const qreal powerOffTimeStepS = 30. / 100.; struct AdvertisementData { // That's what CoreBluetooth has: @@ -115,14 +113,6 @@ QT_END_NAMESPACE QT_USE_NAMESPACE -@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) (PrivateAPI) -// These two methods are scheduled with a small time step -// within a given timeout, they either re-schedule -// themselves or emit a signal/stop some operation. -- (void)stopScan; -- (void)handlePoweredOff; -@end - @implementation QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) -(id)initWithNotifier:(LECBManagerNotifier *)aNotifier @@ -153,60 +143,22 @@ QT_USE_NAMESPACE [super dealloc]; } -- (void)stopScan +- (void)timeout { - using namespace OSXBluetooth; - - // We never schedule stopScan if there is no timeout: - Q_ASSERT(inquiryTimeoutMS > 0); - if (internalState == InquiryActive) { - const int elapsed = scanTimer.elapsed(); - if (elapsed >= inquiryTimeoutMS) { - [manager stopScan]; - [manager setDelegate:nil]; - internalState = InquiryFinished; - Q_ASSERT(notifier); - emit notifier->discoveryFinished(); - } else { - // Re-schedule 'stopScan': - dispatch_queue_t leQueue(qt_LE_queue()); - Q_ASSERT(leQueue); - const int timeChunkMS = std::min(inquiryTimeoutMS - elapsed, timeStepMS); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, - int64_t(timeChunkMS / 1000. * NSEC_PER_SEC)), - leQueue, - ^{ - [self stopScan]; - }); - } - } -} - -- (void)handlePoweredOff -{ - // 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. - using namespace OSXBluetooth; - - if (internalState == InquiryStarting) { - if (errorTimer.elapsed() >= powerOffTimeoutMS) { - [manager setDelegate:nil]; - internalState = ErrorPoweredOff; - Q_ASSERT(notifier); - emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); - } else { - dispatch_queue_t leQueue(qt_LE_queue()); - Q_ASSERT(leQueue); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, - (int64_t)(powerOffTimeStepS * NSEC_PER_SEC)), - leQueue, - ^{ - [self handlePoweredOff]; - }); - } + [manager stopScan]; + [manager setDelegate:nil]; + internalState = InquiryFinished; + Q_ASSERT(notifier); + emit notifier->discoveryFinished(); + } else if (internalState == InquiryStarting) { + // 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 this as an error. + [manager setDelegate:nil]; + internalState = ErrorPoweredOff; + Q_ASSERT(notifier); + emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); } } @@ -233,9 +185,6 @@ QT_USE_NAMESPACE using namespace OSXBluetooth; - dispatch_queue_t leQueue(qt_LE_queue()); - Q_ASSERT(leQueue); - const auto state = central.state; #if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_10_0) || QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13) if (state == CBManagerStatePoweredOn) { @@ -246,18 +195,9 @@ QT_USE_NAMESPACE internalState = InquiryActive; if (inquiryTimeoutMS > 0) { - // We have a finite-length discovery, schedule stopScan, - // with a smaller time step, otherwise it can prevent - // 'self' from being deleted in time, which is not good - // (the block will retain 'self', waiting for timeout). - scanTimer.start(); - const int timeChunkMS = std::min(timeStepMS, inquiryTimeoutMS); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, - int64_t(timeChunkMS / 1000. * NSEC_PER_SEC)), - leQueue, - ^{ - [self stopScan]; - }); + [elapsedTimer cancelTimer]; + elapsedTimer.reset([[GCDTimerObjC alloc] initWithDelegate:self]); + [elapsedTimer startWithTimeout:inquiryTimeoutMS step:timeStepMS]; } [manager scanForPeripheralsWithServices:nil options:nil]; @@ -287,19 +227,15 @@ QT_USE_NAMESPACE 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, + // enable Bluetooth in the "Settings" app. If a user does so, // we'll receive 'PoweredOn' state update later. - // No change in internalState. Wait for 30 seconds - // (we split it into smaller steps not to retain 'self' for - // too long ) ... - errorTimer.start(); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, - (int64_t)(powerOffTimeStepS * NSEC_PER_SEC)), - leQueue, - ^{ - [self handlePoweredOff]; - }); + // No change in internalState. Wait for 30 seconds. + [elapsedTimer cancelTimer]; + elapsedTimer.reset([[GCDTimerObjC alloc] initWithDelegate:self]); + [elapsedTimer startWithTimeout:powerOffTimeoutMS step:300]; return; +#else + Q_UNUSED(powerOffTimeoutMS) #endif internalState = ErrorPoweredOff; emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); @@ -330,6 +266,8 @@ QT_USE_NAMESPACE if (internalState == InquiryActive) [manager stopScan]; + [elapsedTimer cancelTimer]; + [manager setDelegate:nil]; internalState = InquiryCancelled; diff --git a/src/bluetooth/osx/osxbtledeviceinquiry_p.h b/src/bluetooth/osx/osxbtledeviceinquiry_p.h index fc787a6d..a19055ab 100644 --- a/src/bluetooth/osx/osxbtledeviceinquiry_p.h +++ b/src/bluetooth/osx/osxbtledeviceinquiry_p.h @@ -53,10 +53,10 @@ #include "qbluetoothdevicediscoveryagent.h" #include "qbluetoothdeviceinfo.h" +#include "osxbtgcdtimer_p.h" #include "osxbtutility_p.h" #include "osxbluetooth_p.h" -#include #include #include @@ -75,10 +75,8 @@ class LECBManagerNotifier; QT_END_NAMESPACE -// Ugly but all these QT_PREPEND_NAMESPACE etc. are even worse ... -using OSXBluetooth::LECBManagerNotifier; -using OSXBluetooth::ObjCScopedPointer; -using QT_PREPEND_NAMESPACE(QElapsedTimer); +using QT_PREPEND_NAMESPACE(OSXBluetooth)::LECBManagerNotifier; +using QT_PREPEND_NAMESPACE(OSXBluetooth)::ObjCScopedPointer; enum LEInquiryState { @@ -90,7 +88,7 @@ enum LEInquiryState ErrorLENotSupported }; -@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) : NSObject +@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) : NSObject { LECBManagerNotifier *notifier; ObjCScopedPointer manager; @@ -99,16 +97,14 @@ enum LEInquiryState LEInquiryState internalState; int inquiryTimeoutMS; - // Timers to check if we can execute delayed callbacks: - QT_PREPEND_NAMESPACE(QElapsedTimer) errorTimer; - QT_PREPEND_NAMESPACE(QElapsedTimer) scanTimer; + QT_PREPEND_NAMESPACE(OSXBluetooth)::GCDTimer elapsedTimer; } - (id)initWithNotifier:(LECBManagerNotifier *)aNotifier; - (void)dealloc; -// IMPORTANT: both 'startWithTimeout' and 'stop' -// can be executed only on the "Qt's LE queue". +// IMPORTANT: both 'startWithTimeout' and 'stop' MUST be executed on the "Qt's +// LE queue". - (void)startWithTimeout:(int)timeout; - (void)stop; -- cgit v1.2.3