diff options
Diffstat (limited to 'src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm')
-rw-r--r-- | src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm | 177 |
1 files changed, 89 insertions, 88 deletions
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm index ebf9352d..1cfe8286 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm @@ -44,6 +44,7 @@ #include "qbluetoothuuid.h" #include <QtCore/qloggingcategory.h> +#include <QtCore/qscopedpointer.h> #include <QtCore/qdatetime.h> #include <QtCore/qglobal.h> #include <QtCore/qstring.h> @@ -58,15 +59,16 @@ QT_BEGIN_NAMESPACE using OSXBluetooth::ObjCScopedPointer; -class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::DeviceInquiryDelegate, - public OSXBluetooth::LEDeviceInquiryDelegate +class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::DeviceInquiryDelegate { friend class QBluetoothDeviceDiscoveryAgent; friend class OSXBluetooth::DDATimerHandler; public: + typedef QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) LEDeviceInquiryObjC; + QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress & address, QBluetoothDeviceDiscoveryAgent *q); - virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); // Just to make compiler happy. + virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); bool isValid() const; bool isActive() const; @@ -86,12 +88,11 @@ private: void inquiryFinished(IOBluetoothDeviceInquiry *inq) Q_DECL_OVERRIDE; void error(IOBluetoothDeviceInquiry *inq, IOReturn error) Q_DECL_OVERRIDE; void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) Q_DECL_OVERRIDE; - // 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; + + // + void LEinquiryFinished(); + void LEinquiryError(QBluetoothDeviceDiscoveryAgent::Error error); + void LEnotSupported(); // Check if it's a really new device/updated info and emit // q_ptr->deviceDiscovered. @@ -127,7 +128,7 @@ private: typedef QList<QBluetoothDeviceInfo> DevicesList; DevicesList discoveredDevices; - OSXBluetooth::DDATimerHandler timer; + QScopedPointer<OSXBluetooth::DDATimerHandler> timer; }; namespace OSXBluetooth { @@ -137,7 +138,7 @@ DDATimerHandler::DDATimerHandler(QBluetoothDeviceDiscoveryAgentPrivate *d) { Q_ASSERT_X(owner, Q_FUNC_INFO, "invalid pointer"); - timer.setSingleShot(true); + timer.setSingleShot(false); connect(&timer, &QTimer::timeout, this, &DDATimerHandler::onTimer); } @@ -173,8 +174,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con startPending(false), stopPending(false), lastError(QBluetoothDeviceDiscoveryAgent::NoError), - inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), - timer(this) + inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) { Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)"); @@ -192,23 +192,22 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con return; } - // 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 problem. - LEDeviceInquiry newInquiryLE([[LEDeviceInquiryObjC alloc] initWithDelegate:this]); - if (!newInquiryLE) { - qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "failed to " - "initialize a LE inquiry"; - return; - } - hostController.reset(controller.take()); inquiry.reset(newInquiry.take()); - inquiryLE.reset(newInquiryLE.take()); } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() { + if (inquiryLE && agentState != NonActive) { + // 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::isValid() const @@ -218,7 +217,7 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const // (and the error is probably not even related to Bluetooth at all) // - say, allocation error - this is what meant here by valid/invalid. return hostController && [hostController powerState] == kBluetoothHCIPowerStateON - && inquiry && inquiryLE; + && inquiry; } bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const @@ -263,21 +262,29 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE() Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, Q_FUNC_INFO, "called with an invalid Bluetooth adapter"); - agentState = LEScan; + using namespace OSXBluetooth; - // 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.start([LEDeviceInquiryObjC inquiryLength]); + inquiryLE.reset([[LEDeviceInquiryObjC alloc] init]); - if (![inquiryLE start]) { - // We can be here only if we have some kind of resource allocation error, so we - // do not emit finished, we emit error. - timer.stop(); + dispatch_queue_t leQueue(qt_LE_queue()); + if (!leQueue || !inquiryLE) { setError(QBluetoothDeviceDiscoveryAgent::UnknownError, QCoreApplication::translate(DEV_DISCOVERY, DD_NOT_STARTED_LE)); agentState = NonActive; emit q_ptr->error(lastError); } + + 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]; + }); } void QBluetoothDeviceDiscoveryAgentPrivate::stop() @@ -287,6 +294,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError, Q_FUNC_INFO, "called with invalid bluetooth adapter"); + using namespace OSXBluetooth; + const bool prevStart = startPending; startPending = false; stopPending = true; @@ -303,9 +312,16 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() emit q_ptr->error(lastError); } } else { - // Can be asynchronous (depending on a status update of CBCentralManager). - // The call itself is always 'success'. - [inquiryLE stop]; + dispatch_queue_t leQueue(qt_LE_queue()); + Q_ASSERT(leQueue); + // We need the local variable so that it's retained ... + 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(); } } @@ -429,31 +445,45 @@ 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"); - const int timeout = [LEDeviceInquiryObjC inquiryLength]; - Q_ASSERT(timeout > 0); - const QTime scanStartTime([inquiryLE startTime]); - if (scanStartTime.isValid()) { - const int elapsed = scanStartTime.msecsTo(QTime::currentTime()); - Q_ASSERT(elapsed >= 0); - if (elapsed >= timeout) - [inquiryLE stop]; - else - timer.start(timeout - elapsed); - } else { - // Scan not started yet. Wait 5 seconds more. - timer.start(timeout / 2); + 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) << Q_FUNC_INFO << "unexpected inquiry state in LE timeout"; + // Actually, this deserves an assert :) } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDeviceDiscoveryAgent::Error error) +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_X(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError, - Q_FUNC_INFO, "unexpected error code"); - - timer.stop(); + Q_ASSERT(error == QBluetoothDeviceDiscoveryAgent::PoweredOffError); + timer->stop(); + inquiryLE.reset(); agentState = NonActive; setError(error); emit q_ptr->error(lastError); @@ -461,46 +491,17 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryError(QBluetoothDevic void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() { - // Not supported is not an error. + // Not supported is not an error (we still have 'Classic'). qCDebug(QT_BT_OSX) << "no Bluetooth LE support"; - // After we call startLE and before receive NotSupported, - // the user can call stop (setting a pending stop). - // So the same rule apply: - timer.stop(); - - LEdeviceInquiryFinished(); + LEinquiryFinished(); } -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(CBPeripheral *peripheral, const QBluetoothUuid &deviceUuid, - NSDictionary *advertisementData, - NSNumber *RSSI) -{ - Q_ASSERT_X(peripheral, Q_FUNC_INFO, "invalid peripheral (nil)"); - Q_ASSERT_X(agentState == LEScan, Q_FUNC_INFO, - "invalid agent state, expected LE scan"); - - Q_UNUSED(advertisementData) - - QString name; - if (peripheral.name) - name = QString::fromNSString(peripheral.name); - - // 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); - - deviceFound(newDeviceInfo); -} - -void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceInquiryFinished() +void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() { // The same logic as in inquiryFinished, but does not start LE scan. agentState = NonActive; - - timer.stop(); + inquiryLE.reset(); + timer->stop(); if (stopPending && !startPending) { stopPending = false; |