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 <alexander.blasche@qt.io>
This commit is contained in:
parent
c2513dce68
commit
5bdc4e8b0b
|
|
@ -105,6 +105,7 @@
|
|||
- (void)cancelTimer
|
||||
{
|
||||
cancelled = true;
|
||||
timeoutHandler = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -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) <CBCentralManagerDelegate>
|
||||
// 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@
|
|||
|
||||
#include "qbluetoothdevicediscoveryagent.h"
|
||||
#include "qbluetoothdeviceinfo.h"
|
||||
#include "osxbtgcdtimer_p.h"
|
||||
#include "osxbtutility_p.h"
|
||||
#include "osxbluetooth_p.h"
|
||||
|
||||
#include <QtCore/qelapsedtimer.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
|
|
@ -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<CBCentralManagerDelegate, QT_MANGLE_NAMESPACE(GCDTimerDelegate)>
|
||||
{
|
||||
LECBManagerNotifier *notifier;
|
||||
ObjCScopedPointer<CBCentralManager> 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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue