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 | |
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')
24 files changed, 581 insertions, 422 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index b4beda67..b2b7f54c 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -109,6 +109,7 @@ config_bluez:qtHaveModule(dbus) { message("Bluez version is too old to support Bluetooth Low Energy.") message("Only classic Bluetooth will be available.") DEFINES += QT_BLUEZ_NO_BTLE + include(dummy/dummy.pri) SOURCES += \ qlowenergycontroller_p.cpp } @@ -152,14 +153,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,12 +178,10 @@ 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 += \ - qbluetoothdevicediscoveryagent_p.cpp \ qbluetoothlocaldevice_p.cpp \ qbluetoothserviceinfo_p.cpp \ qbluetoothservicediscoveryagent_p.cpp \ diff --git a/src/bluetooth/doc/qtbluetooth.qdocconf b/src/bluetooth/doc/qtbluetooth.qdocconf index 52061d7e..aa485cdb 100644 --- a/src/bluetooth/doc/qtbluetooth.qdocconf +++ b/src/bluetooth/doc/qtbluetooth.qdocconf @@ -47,6 +47,7 @@ exampledirs += ../../../examples/bluetooth \ snippets/ \ ../ +manifestmeta.thumbnail.names = "QtBluetooth/Bluetooth Low Energy Heart Rate Server Example" imagedirs += images diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc index 51c79ca4..f110179f 100644 --- a/src/bluetooth/doc/src/bluetooth-index.qdoc +++ b/src/bluetooth/doc/src/bluetooth-index.qdoc @@ -45,10 +45,13 @@ for transferring data between devices. Bluetooth connectivity is based on basic device management, such as scanning for devices, gathering information about them, and exchanging data between them. -Qt 5.5 contains the first full release of the new Qt Bluetooth -Low Energy API. Further details can be found in the +Qt Bluetooth supports Bluetooth Low Energy development for client/central role use cases. +Further details can be found in the \l {Bluetooth Low Energy Overview}{Bluetooth Low Energy Overview} section. +A new addition in this Qt Bluetooth 5.7 release covers support for Bluetooth Low Energy +applications performing the peripheral/server role. This new API is a Technology Preview. + \section1 Getting Started To use the C++ library in your application, add the following configuration diff --git a/src/bluetooth/doc/src/bluetooth-le-overview.qdoc b/src/bluetooth/doc/src/bluetooth-le-overview.qdoc index a6b8defa..8e78c487 100644 --- a/src/bluetooth/doc/src/bluetooth-le-overview.qdoc +++ b/src/bluetooth/doc/src/bluetooth-le-overview.qdoc @@ -168,19 +168,6 @@ Low Energy devices. The example code below is taken from the \l {heartlistener}{Heart Listener} and \l {heartrate-server}{Heart Rate Server} examples. - \section2 Advertising Services - - If we are implementing a GATT server application on a peripheral device, we need to define the - services we want to offer to central devices and advertise them: - - \snippet heartrate-server/main.cpp Advertising Data - \snippet heartrate-server/main.cpp Start Advertising - \snippet heartrate-server/main.cpp Advertising Data - - Now potential clients can connect to our device, discover the provided service and - register themselves to get notified of changes to the characteristic value. - This part of the API is covered in the following sections. - \section2 Establishing a Connection To be able to read and write the characteristics of the Bluetooth Low Energy peripheral device, @@ -257,4 +244,47 @@ Low Energy devices. \l {https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx}{Bluetooth SIG} whereas others may follow a custom protocol. The above code snippet demonstrates how to the read the standardized HeartRate value. + + \section2 Advertising Services + + If we are implementing a GATT server application on a peripheral device, we need to define the + services we want to offer to central devices and advertise them: + + \snippet heartrate-server/main.cpp Advertising Data + \snippet heartrate-server/main.cpp Start Advertising + + Now potential clients can connect to our device, discover the provided service and + register themselves to get notified of changes to the characteristic value. + This part of the API was already covered by the above sections. + + \section2 Implementing a Service on the Peripheral Device + + The first step is to define the service, its characteristics and descriptors. This is achieved + using the \l QLowEnergyServiceData, \l QLowEnergyCharacteristicData and + \l QLowEnergyDescriptorData classes. These classes act as containers or building blocks for the + essential information that comprises the to-be-defined Bluetooth Low Energy service. + The code snippet below defines a simple HeartRate service which publishes + the measured beats per minute. An example where such a service could be used is a wrist watch. + + \snippet heartrate-server/main.cpp Service Data + + The resulting \c serviceData object can be published as described in the + \l {Advertising Services} section above. Despite the partial information overlap between the + information wrapped by \l QLowEnergyServiceData and \l QLowEnergyAdvertisingData the two classes + serve two very different tasks. The advertising data is published to nearby devices and often + limited in scope due to its size restriction of 29 bytes. Therefore they are not always 100% + complete. By comparison the service data contained inside of \l QLowEnergyServiceData provides + the complete set of service data and only becomes visible to the connecting client when a + connection with an active service discovery has been performed. + + The next section demonstrates how the service can update the heart rate value. Depending on the + nature of the service it may have to comply with the official service definition + as defined on \l {https://www.bluetooth.org}. Other services may be completely custom. The + heart rate service was adopted and its specification can be found under + \l {https://www.bluetooth.com/specifications/adopted-specifications}. + + \snippet heartrate-server/main.cpp Provide Heartbeat + + In general characteristic and descriptor value updates on the peripheral device use the same + methods as connecting Bluetooth Low Energy devices. */ diff --git a/src/bluetooth/lecmaccalculator.cpp b/src/bluetooth/lecmaccalculator.cpp index 1cda9576..47fef7df 100644 --- a/src/bluetooth/lecmaccalculator.cpp +++ b/src/bluetooth/lecmaccalculator.cpp @@ -72,7 +72,7 @@ LeCmacCalculator::LeCmacCalculator() sa.salg_family = AF_ALG; strcpy(reinterpret_cast<char *>(sa.salg_type), "hash"); strcpy(reinterpret_cast<char *>(sa.salg_name), "cmac(aes)"); - if (bind(m_baseSocket, reinterpret_cast<sockaddr *>(&sa), sizeof sa) == -1) { + if (::bind(m_baseSocket, reinterpret_cast<sockaddr *>(&sa), sizeof sa) == -1) { qCWarning(QT_BT_BLUEZ) << "bind() failed for crypto socket:" << strerror(errno); return; } 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/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp index f83f92c9..37a4774d 100644 --- a/src/bluetooth/qbluetooth.cpp +++ b/src/bluetooth/qbluetooth.cpp @@ -76,6 +76,7 @@ namespace QBluetooth { /*! \enum QBluetooth::AttAccessConstraint + \since 5.7 This enum describes the possible requirements for reading or writing an ATT attribute. diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp index b6faee75..3dbbc9e3 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp @@ -40,9 +40,12 @@ #include "qbluetoothdevicediscoveryagent.h" #include "qbluetoothdevicediscoveryagent_p.h" +#include <QtCore/qloggingcategory.h> QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QT_BT) + /*! \class QBluetoothDeviceDiscoveryAgent \inmodule QtBluetooth @@ -88,6 +91,8 @@ QT_BEGIN_NAMESPACE platform. The error is set in response to a call to \l start(). An example for such cases are iOS versions below 5.0 which do not support Bluetooth device search at all. This value was introduced by Qt 5.5. + \value UnsupportedDiscoveryMethod One of the requested discovery methods is not supported by + the current platform. This value was introduced by Qt 5.8. \value UnknownError An unknown error has occurred. */ @@ -110,6 +115,22 @@ QT_BEGIN_NAMESPACE */ /*! + \enum QBluetoothDeviceDiscoveryAgent::DiscoveryMethod + + This enum descibes the type of discovery method employed by the QBluetoothDeviceDiscoveryAgent. + + \value NoMethod The discovery is not possible. None of the available + methods are supported. + \value ClassicMethod The discovery process searches for Bluetooth Classic + (BaseRate) devices. + \value LowEnergyMethod The discovery process searches for Bluetooth Low Energy + devices. + + \sa supportedDiscoveryMethods() + \since 5.8 +*/ + +/*! \fn void QBluetoothDeviceDiscoveryAgent::deviceDiscovered(const QBluetoothDeviceInfo &info) This signal is emitted when the Bluetooth device described by \a info is discovered. @@ -232,16 +253,105 @@ QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() } /*! + Sets the maximum search time for Bluetooth Low Energy device search to + \a timeout in milliseconds. If \a timeout is \c 0 the discovery runs + until \l stop() is called. + + This reflects the fact that the discovery process for Bluetooth Low Energy devices + is mostly open ended. The platform continues to look for more devices until the search is + manually stopped. The timeout ensures that the search is aborted after \a timeout milliseconds. + Of course, it is still possible to manually abort the discovery by calling \l stop(). + + The new timeout value does not take effect until the device search is restarted. + In addition the timeout does not affect the classic Bluetooth device search. Depending on + the platform the classic search may add more time to the total discovery process + beyond \a timeout. + + \sa lowEnergyDiscoveryTimeout() + \since 5.8 + */ +void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout) +{ + Q_D(QBluetoothDeviceDiscoveryAgent); + + // cannot deliberately turn it off + if (d->lowEnergySearchTimeout < 0 || timeout < 0) { + qCDebug(QT_BT) << "The Bluetooth Low Energy device discovery timeout cannot be negative " + "or set on a backend which does not support this feature."; + return; + } + + d->lowEnergySearchTimeout = timeout; +} + +/*! + Returns a timeout in milliseconds that is applied to the Bluetooth Low Energy device search. + A value of \c -1 implies that the platform does not support this property and the timeout for + the device search cannot be adjusted. A return value of \c 0 + implies a never-ending search which must be manually stopped via \l stop(). + + \sa setLowEnergyDiscoveryTimeout() + \since 5.8 + */ +int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const +{ + Q_D(const QBluetoothDeviceDiscoveryAgent); + return d->lowEnergySearchTimeout; +} + +/*! + \fn QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() + + This function returns the discovery methods supported by the current platform. + It can be used to limit the scope of the device discovery. + + \since 5.8 +*/ + +/*! Starts Bluetooth device discovery, if it is not already started. The deviceDiscovered() signal is emitted as each device is discovered. The finished() signal - is emitted once device discovery is complete. + is emitted once device discovery is complete. The discovery utilizes the maximum set of + supported discovery methods on the platform. + + \sa supportedDiscoveryMethods() */ void QBluetoothDeviceDiscoveryAgent::start() { Q_D(QBluetoothDeviceDiscoveryAgent); if (!isActive() && d->lastError != InvalidBluetoothAdapterError) - d->start(); + d->start(supportedDiscoveryMethods()); +} + +/*! + Start Bluetooth device discovery, if it is not already started and the provided + \a methods are supported. + The discovery \a methods limit the scope of the device search. + For example, if the target service or device is a Bluetooth Low Energy device, + this function could be used to limit the search to Bluetooth Low Energy devices and + thereby reduces the discovery time significantly. + + \since 5.8 +*/ +void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods) +{ + if (methods == NoMethod) + return; + + DiscoveryMethods supported = + QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods(); + + Q_D(QBluetoothDeviceDiscoveryAgent); + if (!((supported & methods) == methods)) { + d->lastError = UnsupportedDiscoveryMethod; + d->errorString = QBluetoothDeviceDiscoveryAgent::tr("One or more device discovery methods " + "are not supported on this platform"); + emit error(d->lastError); + } + + if (!isActive() && d->lastError != InvalidBluetoothAdapterError) + d->start(methods); } /*! diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h index 954ae704..84087605 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h @@ -65,6 +65,7 @@ public: PoweredOffError, InvalidBluetoothAdapterError, UnsupportedPlatformError, + UnsupportedDiscoveryMethod, UnknownError = 100 // New errors must be added before Unknown error }; Q_ENUM(Error) @@ -75,6 +76,15 @@ public: }; Q_ENUM(InquiryType) + enum DiscoveryMethod + { + NoMethod = 0x0, + ClassicMethod = 0x01, + LowEnergyMethod = 0x02, + }; + Q_DECLARE_FLAGS(DiscoveryMethods, DiscoveryMethod) + Q_FLAG(DiscoveryMethods) + explicit QBluetoothDeviceDiscoveryAgent(QObject *parent = Q_NULLPTR); explicit QBluetoothDeviceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent = Q_NULLPTR); @@ -91,8 +101,13 @@ public: QList<QBluetoothDeviceInfo> discoveredDevices() const; + void setLowEnergyDiscoveryTimeout(int msTimeout); + int lowEnergyDiscoveryTimeout() const; + + static DiscoveryMethods supportedDiscoveryMethods(); public Q_SLOTS: void start(); + void start(DiscoveryMethods method); void stop(); Q_SIGNALS: @@ -116,6 +131,8 @@ private: #endif }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods) + QT_END_NAMESPACE #endif diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp index 5f163dfd..e3421e08 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp @@ -66,6 +66,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( leScanTimeout(0), pendingCancel(false), pendingStart(false), + lowEnergySearchTimeout(25000), q_ptr(parent) { adapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter", @@ -93,8 +94,16 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const return m_active != NoScanActive; } -void QBluetoothDeviceDiscoveryAgentPrivate::start() +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() { + return (LowEnergyMethod | ClassicMethod); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) +{ + //TODO Implement discovery method handling (see input parameter) + requestedMethods = methods; + if (pendingCancel) { pendingStart = true; return; @@ -192,7 +201,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processSdpDiscoveryFinished() emit q->canceled(); } else if (pendingStart) { pendingStart = pendingCancel = false; - start(); + start(requestedMethods); } else { // check that it didn't finish due to turned off Bluetooth Device const int state = adapter.callMethod<jint>("getState"); @@ -278,15 +287,18 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLowEnergyScan() return; } + // wait interval and sum up what was found if (!leScanTimeout) { leScanTimeout = new QTimer(this); leScanTimeout->setSingleShot(true); - leScanTimeout->setInterval(25000); connect(leScanTimeout, &QTimer::timeout, this, &QBluetoothDeviceDiscoveryAgentPrivate::stopLowEnergyScan); } - leScanTimeout->start(); + if (lowEnergySearchTimeout > 0) { // otherwise no timeout and stop() required + leScanTimeout->setInterval(lowEnergySearchTimeout); + leScanTimeout->start(); + } } void QBluetoothDeviceDiscoveryAgentPrivate::stopLowEnergyScan() diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp index 0243d31f..5288eaf8 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp @@ -68,10 +68,12 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( adapterBluez5(0), discoveryTimer(0), useExtendedDiscovery(false), + lowEnergySearchTimeout(-1), // remains -1 on BlueZ 4 -> timeout not supported q_ptr(parent) { Q_Q(QBluetoothDeviceDiscoveryAgent); if (isBluez5()) { + lowEnergySearchTimeout = 20000; managerBluez5 = new OrgFreedesktopDBusObjectManagerInterface( QStringLiteral("org.bluez"), QStringLiteral("/"), @@ -115,8 +117,17 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const return (adapter || adapterBluez5); } -void QBluetoothDeviceDiscoveryAgentPrivate::start() +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() { + return (ClassicMethod | LowEnergyMethod); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods /*methods*/) +{ + // Currently both BlueZ backends do not distinguish discovery methods. + // The DBus API's always return both device types. Therefore we ignore + // the passed in methods. + if (pendingCancel == true) { pendingStart = true; return; @@ -273,16 +284,18 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startBluez5() } } - // wait 20s and sum up what was found + // wait interval and sum up what was found if (!discoveryTimer) { discoveryTimer = new QTimer(q); discoveryTimer->setSingleShot(true); - discoveryTimer->setInterval(20000); // 20s QObject::connect(discoveryTimer, SIGNAL(timeout()), q, SLOT(_q_discoveryFinished())); } - discoveryTimer->start(); + if (lowEnergySearchTimeout > 0) { // otherwise no timeout and stop() required + discoveryTimer->setInterval(lowEnergySearchTimeout); + discoveryTimer->start(); + } } void QBluetoothDeviceDiscoveryAgentPrivate::stop() @@ -436,7 +449,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_propertyChanged(const QString &na pendingStart = false; pendingCancel = false; - start(); + // start parameter ignored since Bluez 4 doesn't distinguish them + start(QBluetoothDeviceDiscoveryAgent::ClassicMethod + | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); } else { // happens when agent is created while other agent called StopDiscovery() if (!adapter) @@ -514,7 +529,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_discoveryFinished() } else if (pendingStart) { pendingStart = false; pendingCancel = false; - start(); + start(QBluetoothDeviceDiscoveryAgent::ClassicMethod + | QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); } else { emit q->finished(); } diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm index 0e4b460f..6d41ebf0 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, @@ -70,17 +83,16 @@ public: bool isActive() const; - void start(); + void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods m); 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,63 +103,30 @@ 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), lastError(QBluetoothDeviceDiscoveryAgent::NoError), inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), startPending(false), - stopPending(false) + stopPending(false), + lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS) { Q_UNUSED(adapter); + registerQDeviceDiscoveryMetaType(); Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)"); } @@ -158,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]; }); } @@ -175,7 +154,7 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const return inquiryLE; } -void QBluetoothDeviceDiscoveryAgentPrivate::start() +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods /*methods*/) { Q_ASSERT_X(!isActive(), Q_FUNC_INFO, "called on active device discovery agent"); @@ -186,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]; }); } @@ -223,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 @@ -241,7 +231,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDisco Q_FUNC_INFO, "unexpected error"); inquiryLE.reset(); - timer->stop(); startPending = false; stopPending = false; @@ -252,7 +241,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryError(QBluetoothDeviceDisco void QBluetoothDeviceDiscoveryAgentPrivate::LEnotSupported() { inquiryLE.reset(); - timer->stop(); startPending = false; stopPending = false; @@ -281,7 +269,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound(const QBluetoothDevice void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() { inquiryLE.reset(); - timer->stop(); if (stopPending && !startPending) { stopPending = false; @@ -289,47 +276,14 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() } else if (startPending) { startPending = false; stopPending = false; - start(); + // always the same method for start() on iOS + // classic search not supported + start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); } else { emit q_ptr->finished(); } } -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) { @@ -373,7 +327,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); } @@ -399,16 +353,40 @@ QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() return d_ptr->discoveredDevices; } +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() +{ + return LowEnergyMethod; +} + void QBluetoothDeviceDiscoveryAgent::start() { if (d_ptr->lastError != InvalidBluetoothAdapterError) { if (!isActive()) - d_ptr->start(); + d_ptr->start(supportedDiscoveryMethods()); else - qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "already started"; + qCDebug(QT_BT_OSX) << "already started"; } } +void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods) +{ + if (methods == NoMethod) + return; + + DiscoveryMethods supported = + QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods(); + + if (!((supported & methods) == methods)) { + d_ptr->lastError = UnsupportedDiscoveryMethod; + d_ptr->errorString = QBluetoothDeviceDiscoveryAgent::tr("One or more device discovery methods " + "are not supported on this platform"); + emit error(d_ptr->lastError); + } + + if (!isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) + d_ptr->start(methods); +} + void QBluetoothDeviceDiscoveryAgent::stop() { if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) @@ -430,4 +408,21 @@ QString QBluetoothDeviceDiscoveryAgent::errorString() const return d_ptr->errorString; } +int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const +{ + return d_ptr->lowEnergySearchTimeout; +} + +void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout) +{ + // cannot deliberately turn it off + if (timeout < 0) { + qCDebug(QT_BT_OSX) << "The Bluetooth Low Energy device discovery timeout cannot be negative."; + return; + } + + d_ptr->lowEnergySearchTimeout = timeout; + return; +} + QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm index 0ead4e36..ba74da97 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,23 +63,39 @@ 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; - void start(); + void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods m); void startLE(); void stop(); @@ -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,56 +136,22 @@ 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; + QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods; }; -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), @@ -180,8 +160,11 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con startPending(false), stopPending(false), lastError(QBluetoothDeviceDiscoveryAgent::NoError), - inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry) + inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), + lowEnergySearchTimeout(OSXBluetooth::defaultLEScanTimeoutMS) { + registerQDeviceDiscoveryMetaType(); + Q_ASSERT_X(q != Q_NULLPTR, Q_FUNC_INFO, "invalid q_ptr (null)"); HostController controller([[IOBluetoothHostController defaultController] retain]); @@ -207,7 +190,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]; }); } @@ -235,13 +218,16 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const return agentState != NonActive; } -void QBluetoothDeviceDiscoveryAgentPrivate::start() +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) { 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"); + //TODO Implement discovery method handling (see input parameter) + requestedMethods = methods; + if (stopPending) { startPending = true; return; @@ -268,7 +254,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) { @@ -276,18 +278,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]; }); } @@ -320,7 +319,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 @@ -345,7 +344,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished(IOBluetoothDeviceInq } else if (startPending) { startPending = false; stopPending = false; - start(); + start(requestedMethods); } else { // We can be here _only_ if a classic scan // finished in a normal way (not cancelled). @@ -444,49 +443,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); @@ -504,7 +470,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; @@ -512,7 +477,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::LEinquiryFinished() } else if (startPending) { startPending = false; stopPending = false; - start(); //Start from a classic scan again. + start(requestedMethods); //Start again. } else { emit q_ptr->finished(); } @@ -582,12 +547,17 @@ QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() return d_ptr->discoveredDevices; } +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() +{ + return (ClassicMethod | LowEnergyMethod); +} + void QBluetoothDeviceDiscoveryAgent::start() { if (d_ptr->lastError != InvalidBluetoothAdapterError) { if (d_ptr->isValid()) { if (!isActive()) - d_ptr->start(); + d_ptr->start(supportedDiscoveryMethods()); } else { // We previously failed to initialize d_ptr correctly: // either some memory allocation problem or @@ -598,6 +568,26 @@ void QBluetoothDeviceDiscoveryAgent::start() } } +void QBluetoothDeviceDiscoveryAgent::start(DiscoveryMethods methods) +{ + if (methods == NoMethod) + return; + + DiscoveryMethods supported = + QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods(); + + Q_D(QBluetoothDeviceDiscoveryAgent); + if (!((supported & methods) == methods)) { + d_ptr->lastError = UnsupportedDiscoveryMethod; + d_ptr->errorString = QBluetoothDeviceDiscoveryAgent::tr("One or more device discovery methods " + "are not supported on this platform"); + emit error(d_ptr->lastError); + } + + if (!isActive() && d_ptr->lastError != InvalidBluetoothAdapterError) + d_ptr->start(methods); +} + void QBluetoothDeviceDiscoveryAgent::stop() { if (d_ptr->isValid()) { @@ -624,4 +614,21 @@ QString QBluetoothDeviceDiscoveryAgent::errorString() const return d_ptr->errorString; } +void QBluetoothDeviceDiscoveryAgent::setLowEnergyDiscoveryTimeout(int timeout) +{ + // cannot deliberately turn it off + if (timeout < 0) { + qCDebug(QT_BT_OSX) << "The Bluetooth Low Energy device discovery timeout cannot be negative."; + return; + } + + d_ptr->lowEnergySearchTimeout = timeout; + return; +} + +int QBluetoothDeviceDiscoveryAgent::lowEnergyDiscoveryTimeout() const +{ + return d_ptr->lowEnergySearchTimeout; +} + QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_p.cpp index f7780722..e3646db9 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.cpp @@ -42,9 +42,7 @@ #include "qbluetoothaddress.h" #include "qbluetoothuuid.h" -#ifndef QT_IOS_BLUETOOTH #include "dummy/dummy_helper_p.h" -#endif #define QT_DEVICEDISCOVERY_DEBUG @@ -55,12 +53,11 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( QBluetoothDeviceDiscoveryAgent *parent) : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), lastError(QBluetoothDeviceDiscoveryAgent::NoError), + lowEnergySearchTimeout(-1), q_ptr(parent) { Q_UNUSED(deviceAdapter); -#ifndef QT_IOS_BLUETOOTH printDummyWarning(); -#endif } QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() @@ -72,7 +69,12 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const return false; } -void QBluetoothDeviceDiscoveryAgentPrivate::start() +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() +{ + return QBluetoothDeviceDiscoveryAgent::NoMethod; +} + +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods) { Q_Q(QBluetoothDeviceDiscoveryAgent); lastError = QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index c8b15510..06cf29c2 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -99,7 +99,7 @@ public: QBluetoothDeviceDiscoveryAgent *parent); ~QBluetoothDeviceDiscoveryAgentPrivate(); - void start(); + void start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods); void stop(); bool isActive() const; @@ -168,6 +168,8 @@ private: QPointer<QWinRTBluetoothDeviceDiscoveryWorker> worker; #endif + int lowEnergySearchTimeout; + QBluetoothDeviceDiscoveryAgent::DiscoveryMethods requestedMethods; QBluetoothDeviceDiscoveryAgent *q_ptr; }; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index f8a5fca1..bfa39fc1 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -287,6 +287,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), lastError(QBluetoothDeviceDiscoveryAgent::NoError), + lowEnergySearchTimeout(-1), // TODO q_ptr(parent) { Q_UNUSED(deviceAdapter); @@ -302,7 +303,12 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const return worker; } -void QBluetoothDeviceDiscoveryAgentPrivate::start() +QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() +{ + return (ClassicMethod | LowEnergyMethod); +} + +void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods) { if (worker) return; 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.cpp b/src/bluetooth/qlowenergycontroller.cpp index f70e03c6..0095b90a 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -154,6 +154,7 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT) remote device. \value ClosingState The controller is about to be disconnected from the remote device. \value AdvertisingState The controller is currently advertising data. + This value was introduced by Qt 5.7. */ /*! @@ -259,6 +260,7 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT) the other side of the connection requested new parameters. The new values can be retrieved from \a newParameters. + \since 5.7 \sa requestConnectionUpdate() */ @@ -557,9 +559,11 @@ QLowEnergyController::QLowEnergyController( Returns a new object of this class that is in the \l CentralRole and has the parent object \a parent. The \a remoteDevice refers to the device that a connection will be established to later. - * + The controller uses the local default Bluetooth adapter for the connection management. + \sa QLowEnergyController::CentralRole + \since 5.7 */ QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice, QObject *parent) @@ -574,7 +578,9 @@ QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDevice Typically, the next step is to call \l startAdvertising() on the returned object. The controller uses the local default Bluetooth adapter for the connection management. + \sa QLowEnergyController::PeripheralRole + \since 5.7 */ QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent) { @@ -818,6 +824,9 @@ QLowEnergyService *QLowEnergyController::createServiceObject( If this object is currently not in the \l UnconnectedState, nothing happens. \note Advertising will stop automatically once a client connects to the local device. + + \since 5.7 + \sa stopAdvertising() */ void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters ¶meters, const QLowEnergyAdvertisingData &advertisingData, @@ -837,6 +846,9 @@ void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameter /*! Stops advertising, if this object is currently in the advertising state. + + \since 5.7 + \sa startAdvertising() */ void QLowEnergyController::stopAdvertising() { @@ -852,6 +864,8 @@ void QLowEnergyController::stopAdvertising() Constructs and returns a \l QLowEnergyService object with \a parent from \a service. The controller must be in the \l PeripheralRole and in the \l UnconnectedState. The \a service object must be valid. + + \since 5.7 \sa QLowEnergyServiceData::addIncludedService */ QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData &service, @@ -923,6 +937,8 @@ QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData with the actual new parameters. See the \l QLowEnergyConnectionParameters class for more information on connection parameters. \note Currently, this functionality is only implemented on Linux. + + \since 5.7 */ void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶meters) { @@ -952,6 +968,11 @@ QString QLowEnergyController::errorString() const /*! Returns the role that this controller object is in. + + The role is determined when constructing a QLowEnergyController instance + using \l createCentral() or \l createPeripheral(). + + \since 5.7 */ QLowEnergyController::Role QLowEnergyController::role() const { 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 diff --git a/src/bluetooth/qlowenergyservice.cpp b/src/bluetooth/qlowenergyservice.cpp index 1e5c363d..e9722f81 100644 --- a/src/bluetooth/qlowenergyservice.cpp +++ b/src/bluetooth/qlowenergyservice.cpp @@ -229,6 +229,7 @@ QT_BEGIN_NAMESPACE \value LocalService The service is associated with a controller object in the \l{QLowEnergyController::PeripheralRole}{peripheral role}. Such service objects do not change their state. + This value was introduced by Qt 5.7. */ /*! |