diff options
Diffstat (limited to 'src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm')
-rw-r--r-- | src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm | 231 |
1 files changed, 142 insertions, 89 deletions
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm index 1556c5f9..30a6acb6 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm @@ -31,6 +31,7 @@ ** ****************************************************************************/ +#include "qbluetoothdevicediscoverytimer_osx_p.h" #include "qbluetoothdevicediscoveryagent.h" #include "osx/osxbtledeviceinquiry_p.h" #include "qbluetoothlocaldevice.h" @@ -51,27 +52,29 @@ QT_BEGIN_NAMESPACE using OSXBluetooth::ObjCScopedPointer; -class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::LEDeviceInquiryDelegate +class QBluetoothDeviceDiscoveryAgentPrivate { friend class QBluetoothDeviceDiscoveryAgent; + friend class OSXBluetooth::DDATimerHandler; + public: QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &address, QBluetoothDeviceDiscoveryAgent *q); virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); - bool isValid() const; bool isActive() const; void start(); void stop(); private: - // LEDeviceInquiryDelegate: - void LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) Q_DECL_OVERRIDE; - void LEnotSupported() Q_DECL_OVERRIDE; - void LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &deviceUuid, - NSDictionary *advertisementData, NSNumber *RSSI) Q_DECL_OVERRIDE; - void LEdeviceInquiryFinished() Q_DECL_OVERRIDE; + typedef QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) LEDeviceInquiryObjC; + + void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); + void LEnotSupported(); + void LEdeviceFound(const QBluetoothDeviceInfo &info); + void LEinquiryFinished(); + void checkLETimeout(); void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString()); @@ -90,8 +93,45 @@ private: bool startPending; bool stopPending; + + QScopedPointer<OSXBluetooth::DDATimerHandler> timer; }; +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), @@ -103,29 +143,20 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con Q_UNUSED(adapter); Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)"); - - // OSXBTLEDeviceInquiry can be constructed even if LE is not supported - - // at this stage it's only a memory allocation of the object itself, - // if it fails - we have some memory-related problems. - LEDeviceInquiry newInquiryLE([[LEDeviceInquiryObjC alloc] initWithDelegate:this]); - if (!newInquiryLE) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to initialize a device inquiry object"; - return; - } - - inquiryLE.reset(newInquiryLE.take()); } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { -} - -bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const -{ - // isValid() - Qt does not use exceptions, but the ctor - // can fail to initialize some important data-members - // - this is what meant here by valid/invalid. - return inquiryLE; + if (inquiryLE) { + // We want the LE scan to stop as soon as possible. + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + // Local variable to be retained ... + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq stop]; + }); + } + } } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const @@ -135,50 +166,66 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const if (stopPending) return false; - return [inquiryLE isActive]; + return inquiryLE; } void QBluetoothDeviceDiscoveryAgentPrivate::start() { - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "called on invalid device discovery agent"); Q_ASSERT_X(!isActive(), Q_FUNC_INFO, "called on active device discovery agent"); - Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, - Q_FUNC_INFO, "called with an invalid Bluetooth adapter"); if (stopPending) { startPending = true; return; } - discoveredDevices.clear(); - setError(QBluetoothDeviceDiscoveryAgent::NoError); + using namespace OSXBluetooth; - if (![inquiryLE start]) { - // We can be here only if we have some kind of - // resource allocation error. + inquiryLE.reset([[LEDeviceInquiryObjC alloc] init]); + 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); } + + 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]; + }); } void QBluetoothDeviceDiscoveryAgentPrivate::stop() { - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "called on invalid device discovery agent"); Q_ASSERT_X(isActive(), Q_FUNC_INFO, "called whithout active inquiry"); - Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, - Q_FUNC_INFO, "called with invalid bluetooth adapter"); startPending = false; stopPending = true; + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); + Q_ASSERT(leQueue); + setError(QBluetoothDeviceDiscoveryAgent::NoError); - // Can be asynchronous (depending on a status update of CBCentralManager). - // The call itself is always 'success'. - [inquiryLE stop]; + + // Create a local variable - to have a strong referece in a block. + LEDeviceInquiryObjC *inq = inquiryLE.data(); + dispatch_async(leQueue, ^{ + [inq stop]; + }); + // We consider LE scan to be stopped immediately and + // do not care about this LEDeviceInquiry object anymore. + LEinquiryFinished(); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error) { // At the moment the only error reported by osxbtledeviceinquiry // can be 'powered off' error, it happens @@ -187,6 +234,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDevic Q_ASSERT_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError, Q_FUNC_INFO, "unexpected error"); + inquiryLE.reset(); + timer->stop(); + startPending = false; stopPending = false; setError(error); @@ -195,36 +245,17 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDevic void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() { + inquiryLE.reset(); + timer->stop(); + startPending = false; stopPending = false; setError(QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError); emit q_ptr->error(lastError); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &deviceUuid, - NSDictionary *advertisementData, - NSNumber *RSSI) +void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(const QBluetoothDeviceInfo &newDeviceInfo) { - Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); - - QT_BT_MAC_AUTORELEASEPOOL; - - QString name; - if (peripheral.name && peripheral.name.length) { - name = QString::fromNSString(peripheral.name); - } else { - NSString *const localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey]; - if (localName && [localName length]) - name = QString::fromNSString(localName); - } - - // TODO: fix 'classOfDevice' (0 for now). - QBluetoothDeviceInfo newDeviceInfo(deviceUuid, name, 0); - if (RSSI) - newDeviceInfo.setRssi([RSSI shortValue]); - // CoreBluetooth scans only for LE devices. - newDeviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration); - // Update, append or discard. for (int i = 0, e = discoveredDevices.size(); i < e; ++i) { if (discoveredDevices[i].deviceUuid() == newDeviceInfo.deviceUuid()) { @@ -241,9 +272,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(CBPeripheral *peripher emit q_ptr->deviceDiscovered(newDeviceInfo); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryFinished() +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() { - Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid device discovery agent"); + inquiryLE.reset(); + timer->stop(); if (stopPending && !startPending) { stopPending = false; @@ -257,6 +289,41 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryFinished() } } +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) { @@ -329,36 +396,22 @@ QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() void QBluetoothDeviceDiscoveryAgent::start() { if (d_ptr->lastError != InvalidBluetoothAdapterError) { - if (d_ptr->isValid()) { - if (!isActive()) - d_ptr->start(); - else - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already started"; - } else { - // We previously failed to initialize - // private object correctly. - d_ptr->setError(InvalidBluetoothAdapterError); - emit error(InvalidBluetoothAdapterError); - } + if (!isActive()) + d_ptr->start(); + else + qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already started"; } } void QBluetoothDeviceDiscoveryAgent::stop() { - if (d_ptr->isValid()) { - if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) - d_ptr->stop(); - else - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "failed to stop"; - } + if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) + d_ptr->stop(); } bool QBluetoothDeviceDiscoveryAgent::isActive() const { - if (d_ptr->isValid()) - return d_ptr->isActive(); - - return false; + return d_ptr->isActive(); } QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const |