summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@theqtcompany.com>2016-06-10 18:56:56 +0200
committerTimur Pocheptsov <timur.pocheptsov@theqtcompany.com>2016-06-16 16:16:50 +0000
commit05a809736706db8536302e1588f81d7fa2eb93e6 (patch)
treefe5b7f0ff96f876d01422532e1afa70ffd43a88f
parentc37703dcfcd6c9762be046779f136bc8f1c4acd9 (diff)
QtBluetooth - add LE device discovery timeout (OS X/iOS)
- Instead of hardcoded 10 second timeout use the latest Qt API enabling arbitrary or infinite length for LE device discovery. - Since now scan can take long or unlimited time, the previous approach with detached CBCentralManager and DDA is not working anymore - we can not report devices only at the end of scan. Re-use LENotifier and signals/slots instead. - Remove DDA timer, timeout logic is completely inside osxbtledeviceinquiry object now. Change-Id: I07735581ac175d1d5e5d788aaf697dcb41429e31 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
-rw-r--r--src/bluetooth/bluetooth.pro7
-rw-r--r--src/bluetooth/osx/osxbtledeviceinquiry.mm145
-rw-r--r--src/bluetooth/osx/osxbtledeviceinquiry_p.h31
-rw-r--r--src/bluetooth/osx/osxbtnotifier_p.h6
-rw-r--r--src/bluetooth/osx/osxbtutility.mm7
-rw-r--r--src/bluetooth/osx/osxbtutility_p.h16
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm138
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm157
-rw-r--r--src/bluetooth/qbluetoothdevicediscoverytimer_osx_p.h85
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm4
10 files changed, 220 insertions, 376 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index a614516f..3bca5496 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -152,14 +152,10 @@ config_bluez:qtHaveModule(dbus) {
qlowenergycontroller_osx.mm \
qlowenergyservice_osx.mm
- SOURCES += \
- qlowenergycontroller_p.cpp
-
PRIVATE_HEADERS += qbluetoothsocket_osx_p.h \
qbluetoothserver_osx_p.h \
qbluetoothtransferreply_osx_p.h \
qbluetoothtransferreply_osx_p.h \
- qbluetoothdevicediscoverytimer_osx_p.h \
qlowenergycontroller_osx_p.h
SOURCES -= qbluetoothdevicediscoveryagent.cpp
@@ -181,8 +177,7 @@ config_bluez:qtHaveModule(dbus) {
qlowenergyservice_osx.mm
PRIVATE_HEADERS += \
- qlowenergycontroller_osx_p.h \
- qbluetoothdevicediscoverytimer_osx_p.h
+ qlowenergycontroller_osx_p.h
include(osx/osxbt.pri)
SOURCES += \
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
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm
index 571eb793..f2611602 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm
@@ -37,16 +37,17 @@
**
****************************************************************************/
-#include "qbluetoothdevicediscoverytimer_osx_p.h"
#include "qbluetoothdevicediscoveryagent.h"
#include "osx/osxbtledeviceinquiry_p.h"
#include "qbluetoothlocaldevice.h"
#include "qbluetoothdeviceinfo.h"
+#include "osx/osxbtnotifier_p.h"
#include "osx/osxbtutility_p.h"
#include "osx/uistrings_p.h"
#include "qbluetoothuuid.h"
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qobject.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qdebug.h>
@@ -56,12 +57,24 @@
QT_BEGIN_NAMESPACE
-using OSXBluetooth::ObjCScopedPointer;
+namespace
+{
+
+void registerQDeviceDiscoveryMetaType()
+{
+ static bool initDone = false;
+ if (!initDone) {
+ qRegisterMetaType<QBluetoothDeviceInfo>();
+ qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>();
+ initDone = true;
+ }
+}
+
+}//namespace
-class QBluetoothDeviceDiscoveryAgentPrivate
+class QBluetoothDeviceDiscoveryAgentPrivate : public QObject
{
friend class QBluetoothDeviceDiscoveryAgent;
- friend class OSXBluetooth::DDATimerHandler;
public:
QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &address,
@@ -74,13 +87,12 @@ public:
void stop();
private:
- typedef QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) LEDeviceInquiryObjC;
+ using LEDeviceInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry);
void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error);
void LEnotSupported();
void LEdeviceFound(const QBluetoothDeviceInfo &info);
void LEinquiryFinished();
- void checkLETimeout();
void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString());
@@ -91,54 +103,18 @@ private:
QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType;
- typedef ObjCScopedPointer<LEDeviceInquiryObjC> LEDeviceInquiry;
+ using LEDeviceInquiry = OSXBluetooth::ObjCScopedPointer<LEDeviceInquiryObjC>;
LEDeviceInquiry inquiryLE;
- typedef QList<QBluetoothDeviceInfo> DevicesList;
+ using DevicesList = QList<QBluetoothDeviceInfo>;
DevicesList discoveredDevices;
bool startPending;
bool stopPending;
- QScopedPointer<OSXBluetooth::DDATimerHandler> timer;
int lowEnergySearchTimeout;
};
-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),
@@ -146,10 +122,11 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con
inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry),
startPending(false),
stopPending(false),
- lowEnergySearchTimeout(-1) // change when implemented
+ lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS)
{
Q_UNUSED(adapter);
+ registerQDeviceDiscoveryMetaType();
Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)");
}
@@ -160,7 +137,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
// Local variable to be retained ...
LEDeviceInquiryObjC *inq = inquiryLE.data();
- dispatch_async(leQueue, ^{
+ dispatch_sync(leQueue, ^{
[inq stop];
});
}
@@ -188,26 +165,37 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start()
using namespace OSXBluetooth;
- inquiryLE.reset([[LEDeviceInquiryObjC alloc] init]);
+ QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier);
+ // Connections:
+ using ErrMemFunPtr = void (LECBManagerNotifier::*)(QBluetoothDeviceDiscoveryAgent::Error);
+ notifier->connect(notifier.data(), ErrMemFunPtr(&LECBManagerNotifier::CBManagerError),
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError);
+ notifier->connect(notifier.data(), &LECBManagerNotifier::LEnotSupported,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported);
+ notifier->connect(notifier.data(), &LECBManagerNotifier::discoveryFinished,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished);
+ notifier->connect(notifier.data(), &LECBManagerNotifier::deviceDiscovered,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound);
+
+ inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]);
+ if (inquiryLE)
+ notifier.take(); // Whatever happens next, inquiryLE is already the owner ...
+
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);
+ return;
}
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];
+ [inq startWithTimeout:lowEnergySearchTimeout];
});
}
@@ -225,7 +213,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop()
// Create a local variable - to have a strong referece in a block.
LEDeviceInquiryObjC *inq = inquiryLE.data();
- dispatch_async(leQueue, ^{
+ dispatch_sync(leQueue, ^{
[inq stop];
});
// We consider LE scan to be stopped immediately and
@@ -243,7 +231,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDisco
Q_FUNC_INFO, "unexpected error");
inquiryLE.reset();
- timer->stop();
startPending = false;
stopPending = false;
@@ -254,7 +241,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDisco
void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported()
{
inquiryLE.reset();
- timer->stop();
startPending = false;
stopPending = false;
@@ -283,7 +269,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(const QBluetoothDevice
void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished()
{
inquiryLE.reset();
- timer->stop();
if (stopPending && !startPending) {
stopPending = false;
@@ -297,41 +282,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished()
}
}
-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<QBluetoothDeviceInfo> 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)
{
@@ -375,7 +325,7 @@ QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(
d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this))
{
if (!deviceAdapter.isNull()) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "local device address is "
+ qCWarning(QT_BT_OSX) << "local device address is "
"not available, provided address is ignored";
d_ptr->setError(InvalidBluetoothAdapterError);
}
@@ -407,7 +357,7 @@ void QBluetoothDeviceDiscoveryAgent::start()
if (!isActive())
d_ptr->start();
else
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already started";
+ qCDebug(QT_BT_OSX) << "already started";
}
}
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm
index 5422d235..eedeebb5 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm
@@ -37,21 +37,21 @@
**
****************************************************************************/
-#include "qbluetoothdevicediscoverytimer_osx_p.h"
#include "qbluetoothdevicediscoveryagent.h"
#include "osx/osxbtledeviceinquiry_p.h"
#include "osx/osxbtdeviceinquiry_p.h"
#include "qbluetoothlocaldevice.h"
#include "osx/osxbtsdpinquiry_p.h"
#include "qbluetoothdeviceinfo.h"
+#include "osx/osxbtnotifier_p.h"
#include "osx/osxbtutility_p.h"
-#include "osx/uistrings_p.h"
#include "qbluetoothhostinfo.h"
+#include "qbluetoothaddress.h"
+#include "osx/uistrings_p.h"
#include "qbluetoothuuid.h"
#include <QtCore/qloggingcategory.h>
#include <QtCore/qscopedpointer.h>
-#include <QtCore/qdatetime.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qdebug.h>
@@ -63,18 +63,34 @@
QT_BEGIN_NAMESPACE
-using OSXBluetooth::ObjCScopedPointer;
+namespace
+{
-class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::DeviceInquiryDelegate
+void registerQDeviceDiscoveryMetaType()
+{
+ static bool initDone = false;
+ if (!initDone) {
+ qRegisterMetaType<QBluetoothDeviceInfo>();
+ qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>();
+ initDone = true;
+ }
+}
+
+}//namespace
+
+class QBluetoothDeviceDiscoveryAgentPrivate : public QObject,
+ public OSXBluetooth::DeviceInquiryDelegate
{
friend class QBluetoothDeviceDiscoveryAgent;
- friend class OSXBluetooth::DDATimerHandler;
public:
- typedef QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) LEDeviceInquiryObjC;
+ template<class T>
+ using ObjCScopedPointer = OSXBluetooth::ObjCScopedPointer<T>;
+ using LEDeviceInquiryObjC = QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry);
QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress & address,
QBluetoothDeviceDiscoveryAgent *q);
- virtual ~QBluetoothDeviceDiscoveryAgentPrivate();
+
+ ~QBluetoothDeviceDiscoveryAgentPrivate() Q_DECL_OVERRIDE;
bool isValid() const;
bool isActive() const;
@@ -95,7 +111,6 @@ private:
void error(IOBluetoothDeviceInquiry *inq, IOReturn error) Q_DECL_OVERRIDE;
void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) Q_DECL_OVERRIDE;
- //
void LEinquiryFinished();
void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error);
void LEnotSupported();
@@ -105,9 +120,8 @@ private:
void deviceFound(const QBluetoothDeviceInfo &newDeviceInfo);
void setError(IOReturn error, const QString &text = QString());
- void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString());
-
- void checkLETimeout();
+ void setError(QBluetoothDeviceDiscoveryAgent::Error,
+ const QString &text = QString());
QBluetoothDeviceDiscoveryAgent *q_ptr;
AgentState agentState;
@@ -122,57 +136,21 @@ private:
QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType;
- typedef ObjCScopedPointer<DeviceInquiryObjC> DeviceInquiry;
+ using DeviceInquiry = ObjCScopedPointer<DeviceInquiryObjC>;
DeviceInquiry inquiry;
- typedef ObjCScopedPointer<LEDeviceInquiryObjC> LEDeviceInquiry;
+ using LEDeviceInquiry = ObjCScopedPointer<LEDeviceInquiryObjC>;
LEDeviceInquiry inquiryLE;
- typedef ObjCScopedPointer<IOBluetoothHostController> HostController;
+ using HostController = ObjCScopedPointer<IOBluetoothHostController>;
HostController hostController;
- typedef QList<QBluetoothDeviceInfo> DevicesList;
+ using DevicesList = QList<QBluetoothDeviceInfo>;
DevicesList discoveredDevices;
- QScopedPointer<OSXBluetooth::DDATimerHandler> timer;
int lowEnergySearchTimeout;
};
-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) << "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),
@@ -182,8 +160,10 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con
stopPending(false),
lastError(QBluetoothDeviceDiscoveryAgent::NoError),
inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry),
- lowEnergySearchTimeout(-1) // change when implemented
+ lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS)
{
+ registerQDeviceDiscoveryMetaType();
+
Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)");
HostController controller([[IOBluetoothHostController defaultController] retain]);
@@ -209,7 +189,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
// Local variable to be retained ...
LEDeviceInquiryObjC *inq = inquiryLE.data();
- dispatch_async(leQueue, ^{
+ dispatch_sync(leQueue, ^{
[inq stop];
});
}
@@ -270,7 +250,23 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE()
using namespace OSXBluetooth;
- inquiryLE.reset([[LEDeviceInquiryObjC alloc] init]);
+ QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier);
+ // Connections:
+ using ErrMemFunPtr = void (LECBManagerNotifier::*)(QBluetoothDeviceDiscoveryAgent::Error);
+ notifier->connect(notifier.data(), ErrMemFunPtr(&LECBManagerNotifier::CBManagerError),
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError);
+ notifier->connect(notifier.data(), &LECBManagerNotifier::LEnotSupported,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported);
+ notifier->connect(notifier.data(), &LECBManagerNotifier::discoveryFinished,
+ this, &QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished);
+ using DeviceMemFunPtr = void (QBluetoothDeviceDiscoveryAgentPrivate::*)(const QBluetoothDeviceInfo &);
+ notifier->connect(notifier.data(), &LECBManagerNotifier::deviceDiscovered,
+ this, DeviceMemFunPtr(&QBluetoothDeviceDiscoveryAgentPrivate::deviceFound));
+
+ // Check queue and create scanner:
+ inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]);
+ if (inquiryLE)
+ notifier.take(); // Whatever happens next, inquiryLE is already the owner ...
dispatch_queue_t leQueue(qt_LE_queue());
if (!leQueue || !inquiryLE) {
@@ -278,18 +274,15 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE()
QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED_LE));
agentState = NonActive;
emit q_ptr->error(lastError);
+ return;
}
+ // Now start in on LE queue:
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];
+ [inq startWithTimeout:lowEnergySearchTimeout];
});
}
@@ -322,7 +315,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop()
Q_ASSERT(leQueue);
// We need the local variable so that it's retained ...
LEDeviceInquiryObjC *inq = inquiryLE.data();
- dispatch_async(leQueue, ^{
+ dispatch_sync(leQueue, ^{
[inq stop];
});
// We consider LE scan to be stopped immediately and
@@ -446,49 +439,16 @@ void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAg
qCDebug(QT_BT_OSX) << "error set: "<<errorString;
}
-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");
-
- 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<QBluetoothDeviceInfo> 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) << "unexpected inquiry state in LE timeout";
-}
-
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(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError);
- timer->stop();
inquiryLE.reset();
+
+ startPending = false;
+ stopPending = false;
agentState = NonActive;
setError(error);
emit q_ptr->error(lastError);
@@ -506,7 +466,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished()
// The same logic as in inquiryFinished, but does not start LE scan.
agentState = NonActive;
inquiryLE.reset();
- timer->stop();
if (stopPending && !startPending) {
stopPending = false;
diff --git a/src/bluetooth/qbluetoothdevicediscoverytimer_osx_p.h b/src/bluetooth/qbluetoothdevicediscoverytimer_osx_p.h
deleted file mode 100644
index 88906ffd..00000000
--- a/src/bluetooth/qbluetoothdevicediscoverytimer_osx_p.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QBLUETOOTHDEVICEDISCOVERYTIMER_OSX_P_H
-#define QBLUETOOTHDEVICEDISCOVERYTIMER_OSX_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qtimer.h>
-
-QT_BEGIN_NAMESPACE
-
-class QBluetoothDeviceDiscoveryAgentPrivate;
-
-namespace OSXBluetooth {
-
-class DDATimerHandler : public QObject
-{
- Q_OBJECT
-
-public:
- DDATimerHandler(QBluetoothDeviceDiscoveryAgentPrivate *d);
-
- void start(int msec);
- void stop();
-
-private slots:
- void onTimer();
-
-private:
- QTimer timer;
- QBluetoothDeviceDiscoveryAgentPrivate *owner;
-};
-
-}
-
-QT_END_NAMESPACE
-
-#endif // QBLUETOOTHDEVICEDISCOVERYTIMER_OSX_P_H
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index 5c4c7c37..6e85e630 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -103,7 +103,6 @@ ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CB
// TODO: isPrimary is ... always 'NO' - to be investigated.
/*
- using OSXBluetooth::qt_OS_limit;
if (!cbService.isPrimary) {
// Our guess included/not was probably wrong.
newService->type &= ~QLowEnergyService::PrimaryService;
@@ -147,12 +146,9 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl
Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)");
using OSXBluetooth::LECBManagerNotifier;
- using OSXBluetooth::qt_OS_limit;
role = r;
-
-
QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier);
if (role == QLowEnergyController::PeripheralRole) {
#ifndef Q_OS_TVOS