diff options
author | Alex Blasche <alexander.blasche@theqtcompany.com> | 2016-07-13 14:27:55 +0200 |
---|---|---|
committer | Alex Blasche <alexander.blasche@theqtcompany.com> | 2016-07-13 14:27:55 +0200 |
commit | 20949d791463e571bd3a8549933b5b91019f754f (patch) | |
tree | 1dcd9c0c3833aeba40f074b8b26da3656868fa54 /src/bluetooth/osx | |
parent | da0973e98ebd0d81523839ea255015c4a2e646a7 (diff) | |
parent | 1de888375e3bafb44c9cc8dafce68e2b6b4f7a48 (diff) |
Merge remote-tracking branch 'gerrit/dev' into btle
Adds the minimum compile requirements for the new
QBluetoothDeviceDiscoveryAgent API.
Change-Id: Idfe07bee63de9d2849ab68eb455d0be470591795
Diffstat (limited to 'src/bluetooth/osx')
-rw-r--r-- | src/bluetooth/osx/osxbtledeviceinquiry.mm | 145 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtledeviceinquiry_p.h | 31 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtnotifier_p.h | 6 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtutility.mm | 7 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtutility_p.h | 16 |
5 files changed, 117 insertions, 88 deletions
diff --git a/src/bluetooth/osx/osxbtledeviceinquiry.mm b/src/bluetooth/osx/osxbtledeviceinquiry.mm index 58cecccc..7f522c14 100644 --- a/src/bluetooth/osx/osxbtledeviceinquiry.mm +++ b/src/bluetooth/osx/osxbtledeviceinquiry.mm @@ -39,6 +39,7 @@ #include "osxbtledeviceinquiry_p.h" #include "qbluetoothdeviceinfo.h" +#include "osxbtnotifier_p.h" #include "qbluetoothuuid.h" #include "osxbtutility_p.h" @@ -47,6 +48,8 @@ #include "corebluetoothwrapper_p.h" +#include <algorithm> + QT_BEGIN_NAMESPACE namespace OSXBluetooth { @@ -63,30 +66,35 @@ QBluetoothUuid qt_uuid(NSUUID *nsUuid) return QBluetoothUuid(qtUuidData); } -} +const int timeStepMS = 100; +const int powerOffTimeoutMS = 30000; +const qreal powerOffTimeStepS = 30. / 100.; -QT_END_NAMESPACE - -#ifdef QT_NAMESPACE +} -using namespace QT_NAMESPACE; +QT_END_NAMESPACE -#endif +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)init +-(id)initWithNotifier:(LECBManagerNotifier *)aNotifier { if (self = [super init]) { + Q_ASSERT(aNotifier); + notifier = aNotifier; uuids.reset([[NSMutableSet alloc] init]); internalState = InquiryStarting; - state.store(int(internalState)); + inquiryTimeoutMS = OSXBluetooth::defaultLEScanTimeoutMS; } return self; @@ -100,27 +108,36 @@ using namespace QT_NAMESPACE; [manager stopScan]; } + if (notifier) { + notifier->disconnect(); + notifier->deleteLater(); + } + [super dealloc]; } - (void)stopScan { - // Scan's "timeout" - we consider LE device - // discovery finished. using namespace OSXBluetooth; + // We never schedule stopScan if there is no timeout: + Q_ASSERT(inquiryTimeoutMS > 0); + if (internalState == InquiryActive) { - if (scanTimer.elapsed() >= qt_LE_deviceInquiryLength() * 1000) { - // We indeed stop now: + const int elapsed = scanTimer.elapsed(); + if (elapsed >= inquiryTimeoutMS) { [manager stopScan]; [manager setDelegate:nil]; internalState = InquiryFinished; - state.store(int(internalState)); + 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(qt_LE_deviceInquiryLength() / 100. * NSEC_PER_SEC)), + int64_t(timeChunkMS / 1000. * NSEC_PER_SEC)), leQueue, ^{ [self stopScan]; @@ -135,30 +152,32 @@ using namespace QT_NAMESPACE; // 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() >= 30000) { + if (errorTimer.elapsed() >= powerOffTimeoutMS) { [manager setDelegate:nil]; internalState = ErrorPoweredOff; - state.store(int(internalState)); + Q_ASSERT(notifier); + emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); } else { - dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + dispatch_queue_t leQueue(qt_LE_queue()); Q_ASSERT(leQueue); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, - (int64_t)(30 / 100. * NSEC_PER_SEC)), + (int64_t)(powerOffTimeStepS * NSEC_PER_SEC)), leQueue, ^{ [self handlePoweredOff]; }); - } } } -- (void)start +- (void)startWithTimeout:(int)timeout { dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); - Q_ASSERT(leQueue); + inquiryTimeoutMS = timeout; manager.reset([[CBCentralManager alloc] initWithDelegate:self queue:leQueue]); } @@ -170,6 +189,8 @@ using namespace QT_NAMESPACE; if (internalState != InquiryActive && internalState != InquiryStarting) return; + Q_ASSERT(notifier); + using namespace OSXBluetooth; dispatch_queue_t leQueue(qt_LE_queue()); @@ -179,39 +200,50 @@ using namespace QT_NAMESPACE; 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]; - }); + + 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]; + }); + } + [manager scanForPeripheralsWithServices:nil options:nil]; } // Else we ignore. - } else if (state == CBCentralManagerStateUnsupported || state == CBCentralManagerStateUnauthorized) { + } else if (cbState == CBCentralManagerStateUnsupported + || cbState == CBCentralManagerStateUnauthorized) { if (internalState == InquiryActive) { [manager stopScan]; - // Not sure how this is possible at all, probably, can never happen. + // Not sure how this is possible at all, + // probably, can never happen. internalState = ErrorPoweredOff; + emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); } else { internalState = ErrorLENotSupported; + emit notifier->LEnotSupported(); } [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, + // 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. - // No change in state. Wait for 30 seconds (we split it into 'chunks' not - // to retain 'self' for too long ) ... + // 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)(30 / 100. * NSEC_PER_SEC)), + (int64_t)(powerOffTimeStepS * NSEC_PER_SEC)), leQueue, ^{ [self handlePoweredOff]; @@ -219,9 +251,10 @@ using namespace QT_NAMESPACE; return; #endif internalState = ErrorPoweredOff; + emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); } else { - internalState = ErrorPoweredOff; [manager stopScan]; + emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); } [manager setDelegate:nil]; @@ -237,8 +270,6 @@ using namespace QT_NAMESPACE; // lost; an update is imminent. " // Wait for this imminent update. } - - state.store(int(internalState)); } - (void)stop @@ -248,11 +279,16 @@ using namespace QT_NAMESPACE; [manager setDelegate:nil]; internalState = InquiryCancelled; - state.store(int(internalState)); + + notifier->disconnect(); + notifier->deleteLater(); + notifier = nullptr; } -- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral - advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI +- (void)centralManager:(CBCentralManager *)central + didDiscoverPeripheral:(CBPeripheral *)peripheral + advertisementData:(NSDictionary *)advertisementData + RSSI:(NSNumber *)RSSI { Q_UNUSED(advertisementData); @@ -264,9 +300,10 @@ using namespace QT_NAMESPACE; if (internalState != InquiryActive) return; - QBluetoothUuid deviceUuid; - + if (!notifier) + return; + QBluetoothUuid deviceUuid; if (!peripheral.identifier) { qCWarning(QT_BT_OSX) << "peripheral without NSUUID"; @@ -274,7 +311,9 @@ using namespace QT_NAMESPACE; } if ([uuids containsObject:peripheral.identifier]) { - // We already know this peripheral ... + // TODO: my understanding of the same peripheral reported many times seems + // to be outdated or even wrong - nowadays it's reported twice and the + // second time (AFAIK) more info can be extracted ... return; } @@ -296,17 +335,7 @@ using namespace QT_NAMESPACE; newDeviceInfo.setRssi([RSSI shortValue]); // CoreBluetooth scans only for LE devices. newDeviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); - devices.append(newDeviceInfo); -} - -- (LEInquiryState) inquiryState -{ - return LEInquiryState(state.load()); -} - -- (const QList<QBluetoothDeviceInfo> &)discoveredDevices -{ - return devices; + emit notifier->deviceDiscovered(newDeviceInfo); } @end diff --git a/src/bluetooth/osx/osxbtledeviceinquiry_p.h b/src/bluetooth/osx/osxbtledeviceinquiry_p.h index 24bf181e..d79272be 100644 --- a/src/bluetooth/osx/osxbtledeviceinquiry_p.h +++ b/src/bluetooth/osx/osxbtledeviceinquiry_p.h @@ -57,7 +57,6 @@ #include <QtCore/qelapsedtimer.h> #include <QtCore/qglobal.h> -#include <QtCore/qatomic.h> #include <QtCore/qlist.h> #include <Foundation/Foundation.h> @@ -69,8 +68,20 @@ QT_BEGIN_NAMESPACE class QBluetoothUuid; +namespace OSXBluetooth +{ + +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); + enum LEInquiryState { InquiryStarting, @@ -83,29 +94,27 @@ enum LEInquiryState @interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) : NSObject { - QT_PREPEND_NAMESPACE(OSXBluetooth)::ObjCScopedPointer<NSMutableSet> uuids; - QT_PREPEND_NAMESPACE(OSXBluetooth)::ObjCScopedPointer<CBCentralManager> manager; + LECBManagerNotifier *notifier; + ObjCScopedPointer<NSMutableSet> uuids; + ObjCScopedPointer<CBCentralManager> manager; QList<QBluetoothDeviceInfo> devices; - LEInquiryState internalState; - QT_PREPEND_NAMESPACE(QAtomicInt) state; + int inquiryTimeoutMS; // Timers to check if we can execute delayed callbacks: QT_PREPEND_NAMESPACE(QElapsedTimer) errorTimer; QT_PREPEND_NAMESPACE(QElapsedTimer) scanTimer; } -- (id)init; +- (id)initWithNotifier:(LECBManagerNotifier *)aNotifier; - (void)dealloc; -// IMPORTANT: both 'start' and 'stop' are to be executed on the "Qt's LE queue". -- (void)start; +// IMPORTANT: both 'startWithTimeout' and 'stop' +// can be executed only on the "Qt's LE queue". +- (void)startWithTimeout:(int)timeout; - (void)stop; -- (LEInquiryState)inquiryState; -- (const QList<QBluetoothDeviceInfo> &)discoveredDevices; - @end #endif diff --git a/src/bluetooth/osx/osxbtnotifier_p.h b/src/bluetooth/osx/osxbtnotifier_p.h index 3059e2d3..0ffd7f51 100644 --- a/src/bluetooth/osx/osxbtnotifier_p.h +++ b/src/bluetooth/osx/osxbtnotifier_p.h @@ -52,7 +52,9 @@ // +#include "qbluetoothdevicediscoveryagent.h" #include "qlowenergycontroller.h" +#include "qbluetoothdeviceinfo.h" #include "qbluetoothuuid.h" #include "qbluetooth.h" @@ -73,6 +75,9 @@ class LECBManagerNotifier : public QObject Q_OBJECT Q_SIGNALS: + void deviceDiscovered(QBluetoothDeviceInfo deviceInfo); + void discoveryFinished(); + void connected(); void disconnected(); @@ -85,6 +90,7 @@ Q_SIGNALS: void descriptorWritten(QLowEnergyHandle descHandle, const QByteArray &value); void LEnotSupported(); + void CBManagerError(QBluetoothDeviceDiscoveryAgent::Error error); void CBManagerError(QLowEnergyController::Error error); void CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error error); void CBManagerError(const QBluetoothUuid &serviceUuid, QLowEnergyService::ServiceError error); diff --git a/src/bluetooth/osx/osxbtutility.mm b/src/bluetooth/osx/osxbtutility.mm index f579b2a4..29159cdb 100644 --- a/src/bluetooth/osx/osxbtutility.mm +++ b/src/bluetooth/osx/osxbtutility.mm @@ -68,6 +68,8 @@ Q_LOGGING_CATEGORY(QT_BT_OSX, "qt.bluetooth.ios") namespace OSXBluetooth { +const int defaultLEScanTimeoutMS = 25000; + QString qt_address(NSString *address) { if (address && address.length) { @@ -348,11 +350,6 @@ dispatch_queue_t qt_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 5e616b74..28ecfec9 100644 --- a/src/bluetooth/osx/osxbtutility_p.h +++ b/src/bluetooth/osx/osxbtutility_p.h @@ -53,7 +53,6 @@ #include <QtCore/qloggingcategory.h> #include <QtCore/qscopedpointer.h> -#include <QtCore/qsysinfo.h> #include <QtCore/qglobal.h> #include <Foundation/Foundation.h> @@ -302,20 +301,9 @@ QByteArray qt_bytearray(NSData *data); QByteArray qt_bytearray(NSObject *data); ObjCStrongReference<NSData> data_from_bytearray(const QByteArray & qtData); -inline QSysInfo::MacVersion qt_OS_limit(QSysInfo::MacVersion osxVersion, QSysInfo::MacVersion iosVersion) -{ -#ifdef Q_OS_OSX - Q_UNUSED(iosVersion) - return osxVersion; -#else - Q_UNUSED(osxVersion) - return iosVersion; -#endif -} - dispatch_queue_t qt_LE_queue(); -// LE scan, in seconds. -unsigned qt_LE_deviceInquiryLength(); + +extern const int defaultLEScanTimeoutMS; } // namespace OSXBluetooth |