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
|
- (void)cancelTimer
|
||||||
{
|
{
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
|
timeoutHandler = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,7 @@ QBluetoothUuid qt_uuid(NSUUID *nsUuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
const int timeStepMS = 100;
|
const int timeStepMS = 100;
|
||||||
|
|
||||||
const int powerOffTimeoutMS = 30000;
|
const int powerOffTimeoutMS = 30000;
|
||||||
const qreal powerOffTimeStepS = 30. / 100.;
|
|
||||||
|
|
||||||
struct AdvertisementData {
|
struct AdvertisementData {
|
||||||
// That's what CoreBluetooth has:
|
// That's what CoreBluetooth has:
|
||||||
|
|
@ -115,14 +113,6 @@ QT_END_NAMESPACE
|
||||||
|
|
||||||
QT_USE_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)
|
@implementation QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry)
|
||||||
|
|
||||||
-(id)initWithNotifier:(LECBManagerNotifier *)aNotifier
|
-(id)initWithNotifier:(LECBManagerNotifier *)aNotifier
|
||||||
|
|
@ -153,60 +143,22 @@ QT_USE_NAMESPACE
|
||||||
[super dealloc];
|
[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) {
|
if (internalState == InquiryActive) {
|
||||||
const int elapsed = scanTimer.elapsed();
|
[manager stopScan];
|
||||||
if (elapsed >= inquiryTimeoutMS) {
|
[manager setDelegate:nil];
|
||||||
[manager stopScan];
|
internalState = InquiryFinished;
|
||||||
[manager setDelegate:nil];
|
Q_ASSERT(notifier);
|
||||||
internalState = InquiryFinished;
|
emit notifier->discoveryFinished();
|
||||||
Q_ASSERT(notifier);
|
} else if (internalState == InquiryStarting) {
|
||||||
emit notifier->discoveryFinished();
|
// This is interesting on iOS only, where the system shows an alert
|
||||||
} else {
|
// asking to enable Bluetooth in the 'Settings' app. If not done yet
|
||||||
// Re-schedule 'stopScan':
|
// (after 30 seconds) - we consider this as an error.
|
||||||
dispatch_queue_t leQueue(qt_LE_queue());
|
[manager setDelegate:nil];
|
||||||
Q_ASSERT(leQueue);
|
internalState = ErrorPoweredOff;
|
||||||
const int timeChunkMS = std::min(inquiryTimeoutMS - elapsed, timeStepMS);
|
Q_ASSERT(notifier);
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
|
emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
|
||||||
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];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,9 +185,6 @@ QT_USE_NAMESPACE
|
||||||
|
|
||||||
using namespace OSXBluetooth;
|
using namespace OSXBluetooth;
|
||||||
|
|
||||||
dispatch_queue_t leQueue(qt_LE_queue());
|
|
||||||
Q_ASSERT(leQueue);
|
|
||||||
|
|
||||||
const auto state = central.state;
|
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 QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_10_0) || QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13)
|
||||||
if (state == CBManagerStatePoweredOn) {
|
if (state == CBManagerStatePoweredOn) {
|
||||||
|
|
@ -246,18 +195,9 @@ QT_USE_NAMESPACE
|
||||||
internalState = InquiryActive;
|
internalState = InquiryActive;
|
||||||
|
|
||||||
if (inquiryTimeoutMS > 0) {
|
if (inquiryTimeoutMS > 0) {
|
||||||
// We have a finite-length discovery, schedule stopScan,
|
[elapsedTimer cancelTimer];
|
||||||
// with a smaller time step, otherwise it can prevent
|
elapsedTimer.reset([[GCDTimerObjC alloc] initWithDelegate:self]);
|
||||||
// 'self' from being deleted in time, which is not good
|
[elapsedTimer startWithTimeout:inquiryTimeoutMS step:timeStepMS];
|
||||||
// (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];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[manager scanForPeripheralsWithServices:nil options:nil];
|
[manager scanForPeripheralsWithServices:nil options:nil];
|
||||||
|
|
@ -287,19 +227,15 @@ QT_USE_NAMESPACE
|
||||||
if (internalState == InquiryStarting) {
|
if (internalState == InquiryStarting) {
|
||||||
#ifndef Q_OS_OSX
|
#ifndef Q_OS_OSX
|
||||||
// On iOS a user can see at this point an alert asking to
|
// 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.
|
// we'll receive 'PoweredOn' state update later.
|
||||||
// No change in internalState. Wait for 30 seconds
|
// No change in internalState. Wait for 30 seconds.
|
||||||
// (we split it into smaller steps not to retain 'self' for
|
[elapsedTimer cancelTimer];
|
||||||
// too long ) ...
|
elapsedTimer.reset([[GCDTimerObjC alloc] initWithDelegate:self]);
|
||||||
errorTimer.start();
|
[elapsedTimer startWithTimeout:powerOffTimeoutMS step:300];
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
|
|
||||||
(int64_t)(powerOffTimeStepS * NSEC_PER_SEC)),
|
|
||||||
leQueue,
|
|
||||||
^{
|
|
||||||
[self handlePoweredOff];
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(powerOffTimeoutMS)
|
||||||
#endif
|
#endif
|
||||||
internalState = ErrorPoweredOff;
|
internalState = ErrorPoweredOff;
|
||||||
emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
|
emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError);
|
||||||
|
|
@ -330,6 +266,8 @@ QT_USE_NAMESPACE
|
||||||
if (internalState == InquiryActive)
|
if (internalState == InquiryActive)
|
||||||
[manager stopScan];
|
[manager stopScan];
|
||||||
|
|
||||||
|
[elapsedTimer cancelTimer];
|
||||||
|
|
||||||
[manager setDelegate:nil];
|
[manager setDelegate:nil];
|
||||||
internalState = InquiryCancelled;
|
internalState = InquiryCancelled;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,10 @@
|
||||||
|
|
||||||
#include "qbluetoothdevicediscoveryagent.h"
|
#include "qbluetoothdevicediscoveryagent.h"
|
||||||
#include "qbluetoothdeviceinfo.h"
|
#include "qbluetoothdeviceinfo.h"
|
||||||
|
#include "osxbtgcdtimer_p.h"
|
||||||
#include "osxbtutility_p.h"
|
#include "osxbtutility_p.h"
|
||||||
#include "osxbluetooth_p.h"
|
#include "osxbluetooth_p.h"
|
||||||
|
|
||||||
#include <QtCore/qelapsedtimer.h>
|
|
||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
#include <QtCore/qlist.h>
|
#include <QtCore/qlist.h>
|
||||||
|
|
||||||
|
|
@ -75,10 +75,8 @@ class LECBManagerNotifier;
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
// Ugly but all these QT_PREPEND_NAMESPACE etc. are even worse ...
|
using QT_PREPEND_NAMESPACE(OSXBluetooth)::LECBManagerNotifier;
|
||||||
using OSXBluetooth::LECBManagerNotifier;
|
using QT_PREPEND_NAMESPACE(OSXBluetooth)::ObjCScopedPointer;
|
||||||
using OSXBluetooth::ObjCScopedPointer;
|
|
||||||
using QT_PREPEND_NAMESPACE(QElapsedTimer);
|
|
||||||
|
|
||||||
enum LEInquiryState
|
enum LEInquiryState
|
||||||
{
|
{
|
||||||
|
|
@ -90,7 +88,7 @@ enum LEInquiryState
|
||||||
ErrorLENotSupported
|
ErrorLENotSupported
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) : NSObject
|
@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) : NSObject<CBCentralManagerDelegate, QT_MANGLE_NAMESPACE(GCDTimerDelegate)>
|
||||||
{
|
{
|
||||||
LECBManagerNotifier *notifier;
|
LECBManagerNotifier *notifier;
|
||||||
ObjCScopedPointer<CBCentralManager> manager;
|
ObjCScopedPointer<CBCentralManager> manager;
|
||||||
|
|
@ -99,16 +97,14 @@ enum LEInquiryState
|
||||||
LEInquiryState internalState;
|
LEInquiryState internalState;
|
||||||
int inquiryTimeoutMS;
|
int inquiryTimeoutMS;
|
||||||
|
|
||||||
// Timers to check if we can execute delayed callbacks:
|
QT_PREPEND_NAMESPACE(OSXBluetooth)::GCDTimer elapsedTimer;
|
||||||
QT_PREPEND_NAMESPACE(QElapsedTimer) errorTimer;
|
|
||||||
QT_PREPEND_NAMESPACE(QElapsedTimer) scanTimer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithNotifier:(LECBManagerNotifier *)aNotifier;
|
- (id)initWithNotifier:(LECBManagerNotifier *)aNotifier;
|
||||||
- (void)dealloc;
|
- (void)dealloc;
|
||||||
|
|
||||||
// IMPORTANT: both 'startWithTimeout' and 'stop'
|
// IMPORTANT: both 'startWithTimeout' and 'stop' MUST be executed on the "Qt's
|
||||||
// can be executed only on the "Qt's LE queue".
|
// LE queue".
|
||||||
- (void)startWithTimeout:(int)timeout;
|
- (void)startWithTimeout:(int)timeout;
|
||||||
- (void)stop;
|
- (void)stop;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue