diff options
author | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2022-11-10 22:42:26 +0200 |
---|---|---|
committer | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2022-11-10 22:42:26 +0200 |
commit | e957d4810b05d8453e163d7bcdcab42d3c60a7bb (patch) | |
tree | 76ed3d969c478042f8ca747bf425b0e5d2e893dc /src/bluetooth | |
parent | 47c424c7990061714bd3108e3fbaa4339ba411b9 (diff) | |
parent | a6f5b79169991ffde38643d7b1a04d6a9de137bf (diff) | |
download | qtconnectivity-e957d4810b05d8453e163d7bcdcab42d3c60a7bb.tar.gz |
Merge remote-tracking branch 'origin/tqtc/lts-5.15.8' into tqtc/lts-5.15-opensourcev5.15.8-lts-lgpl
Change-Id: I4d53774cfe0783f50a123158d8bac7a02c382968
Diffstat (limited to 'src/bluetooth')
-rw-r--r-- | src/bluetooth/osx/osxbtconnectionmonitor.mm | 24 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtconnectionmonitor_p.h | 2 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtutility.mm | 19 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtutility_p.h | 6 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm | 21 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothlocaldevice_osx.mm | 6 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothsocket_winrt.cpp | 46 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_darwin.mm | 65 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_darwin_p.h | 5 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_winrt.cpp | 7 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_winrt_new.cpp | 10 |
11 files changed, 155 insertions, 56 deletions
diff --git a/src/bluetooth/osx/osxbtconnectionmonitor.mm b/src/bluetooth/osx/osxbtconnectionmonitor.mm index b777af8e..f41bbed5 100644 --- a/src/bluetooth/osx/osxbtconnectionmonitor.mm +++ b/src/bluetooth/osx/osxbtconnectionmonitor.mm @@ -81,14 +81,8 @@ using namespace QT_NAMESPACE; - (void)dealloc { - [discoveryNotification unregister]; - [discoveryNotification release]; - - for (IOBluetoothUserNotification *n in foundConnections) - [n unregister]; - - [foundConnections release]; - + Q_ASSERT_X(!monitor, "-dealloc", + "Connection monitor was not stopped, calling -stopMonitoring is required"); [super dealloc]; } @@ -137,4 +131,18 @@ using namespace QT_NAMESPACE; monitor->deviceDisconnected(deviceAddress); } +- (void)stopMonitoring +{ + monitor = nullptr; + [discoveryNotification unregister]; + [discoveryNotification release]; + discoveryNotification = nil; + + for (IOBluetoothUserNotification *n in foundConnections) + [n unregister]; + + [foundConnections release]; + foundConnections = nil; +} + @end diff --git a/src/bluetooth/osx/osxbtconnectionmonitor_p.h b/src/bluetooth/osx/osxbtconnectionmonitor_p.h index 679f6124..50dc9d77 100644 --- a/src/bluetooth/osx/osxbtconnectionmonitor_p.h +++ b/src/bluetooth/osx/osxbtconnectionmonitor_p.h @@ -84,6 +84,8 @@ QT_END_NAMESPACE - (void)connectionNotification:(id)notification withDevice:(IOBluetoothDevice *)device; - (void)connectionClosedNotification:(id)notification withDevice:(IOBluetoothDevice *)device; +- (void)stopMonitoring; + @end #endif diff --git a/src/bluetooth/osx/osxbtutility.mm b/src/bluetooth/osx/osxbtutility.mm index c7fa7c42..3d41a224 100644 --- a/src/bluetooth/osx/osxbtutility.mm +++ b/src/bluetooth/osx/osxbtutility.mm @@ -42,6 +42,7 @@ #include "osxbtutility_p.h" #include "qbluetoothuuid.h" +#include <QtCore/qoperatingsystemversion.h> #include <QtCore/qendian.h> #include <QtCore/qstring.h> @@ -76,6 +77,8 @@ const int defaultLEScanTimeoutMS = 25000; // We use it only on iOS for now: const int maxValueLength = 512; +NSString *const bluetoothUsageKey = @"NSBluetoothAlwaysUsageDescription"; + QString qt_address(NSString *address) { if (address && address.length) { @@ -351,6 +354,22 @@ ObjCStrongReference<NSMutableData> mutable_data_from_bytearray(const QByteArray return result; } +bool qt_appNeedsBluetoothUsageDescription() +{ +#ifdef Q_OS_MACOS + return QOperatingSystemVersion::current() > QOperatingSystemVersion::MacOSBigSur; +#endif + return true; +} + +bool qt_appPlistContainsDescription(NSString *key) +{ + Q_ASSERT(key); + + NSDictionary<NSString *, id> *infoDict = NSBundle.mainBundle.infoDictionary; + return !!infoDict[key]; +} + // A small RAII class for a dispatch queue. class SerialDispatchQueue { diff --git a/src/bluetooth/osx/osxbtutility_p.h b/src/bluetooth/osx/osxbtutility_p.h index c2bc6cf8..1b1d44be 100644 --- a/src/bluetooth/osx/osxbtutility_p.h +++ b/src/bluetooth/osx/osxbtutility_p.h @@ -307,6 +307,12 @@ dispatch_queue_t qt_LE_queue(); extern const int defaultLEScanTimeoutMS; extern const int maxValueLength; +// Add more keys if needed, for now this one is enough: +extern NSString *const bluetoothUsageKey; + +bool qt_appNeedsBluetoothUsageDescription(); +bool qt_appPlistContainsDescription(NSString *key); + } // namespace OSXBluetooth // Logging category for both OS X and iOS. diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm index d9883d28..a5cb034a 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm @@ -144,6 +144,8 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) { + using namespace OSXBluetooth; + Q_ASSERT(!isActive()); Q_ASSERT(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); Q_ASSERT(methods & (QBluetoothDeviceDiscoveryAgent::ClassicMethod @@ -157,6 +159,25 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent } #endif // Q_OS_MACOS + // To be able to scan for devices, iOS requires Info.plist containing + // NSBluetoothAlwaysUsageDescription entry with a string, explaining + // the usage of Bluetooth interface. macOS also requires this description, + // starting from Monterey. + + // No Classic on iOS, and Classic does not require a description on macOS: + if (methods.testFlag(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) + && qt_appNeedsBluetoothUsageDescription() + && !qt_appPlistContainsDescription(bluetoothUsageKey)) { + // This would result in Bluetooth framework throwing an exception + // the moment we try to start device discovery. + qCWarning(QT_BT_OSX) + << "A proper Info.plist with NSBluetoothAlwaysUsageDescription " + "entry is required, cannot start device discovery"; + setError(QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); + emit q_ptr->error(lastError); + return; + } + requestedMethods = methods; if (stopPending) { diff --git a/src/bluetooth/qbluetoothlocaldevice_osx.mm b/src/bluetooth/qbluetoothlocaldevice_osx.mm index e7dd9906..2bf467f8 100644 --- a/src/bluetooth/qbluetoothlocaldevice_osx.mm +++ b/src/bluetooth/qbluetoothlocaldevice_osx.mm @@ -66,6 +66,7 @@ public: QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *, const QBluetoothAddress & = QBluetoothAddress()); + ~QBluetoothLocalDevicePrivate(); bool isValid() const; void requestPairing(const QBluetoothAddress &address, Pairing pairing); @@ -147,6 +148,11 @@ QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice connectionMonitor.reset([[ObjCConnectionMonitor alloc] initWithMonitor:this]); } +QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() +{ + [connectionMonitor stopMonitoring]; +} + bool QBluetoothLocalDevicePrivate::isValid() const { return hostController.data(); diff --git a/src/bluetooth/qbluetoothsocket_winrt.cpp b/src/bluetooth/qbluetoothsocket_winrt.cpp index 48b14757..d562f6f7 100644 --- a/src/bluetooth/qbluetoothsocket_winrt.cpp +++ b/src/bluetooth/qbluetoothsocket_winrt.cpp @@ -576,6 +576,17 @@ QString QBluetoothSocketPrivateWinRT::localName() const return device.name(); } +static QString fromWinApiAddress(HString address) +{ + // WinAPI returns address with parentheses around it. We need to remove + // them to convert to QBluetoothAddress. + QString addressStr(qt_QStringFromHString(address)); + if (addressStr.startsWith(QLatin1Char('(')) && addressStr.endsWith(QLatin1Char(')'))) { + addressStr = addressStr.mid(1, addressStr.size() - 2); + } + return addressStr; +} + QBluetoothAddress QBluetoothSocketPrivateWinRT::localAddress() const { if (!m_socketObject) @@ -588,10 +599,13 @@ QBluetoothAddress QBluetoothSocketPrivateWinRT::localAddress() const ComPtr<IHostName> localHost; hr = info->get_LocalAddress(&localHost); Q_ASSERT_SUCCEEDED(hr); - HString localAddress; - hr = localHost->get_CanonicalName(localAddress.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - return QBluetoothAddress(qt_QStringFromHString(localAddress)); + if (localHost) { + HString localAddress; + hr = localHost->get_CanonicalName(localAddress.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + return QBluetoothAddress(fromWinApiAddress(std::move(localAddress))); + } + return QBluetoothAddress(); } quint16 QBluetoothSocketPrivateWinRT::localPort() const @@ -627,10 +641,13 @@ QString QBluetoothSocketPrivateWinRT::peerName() const ComPtr<IHostName> remoteHost; hr = info->get_RemoteHostName(&remoteHost); Q_ASSERT_SUCCEEDED(hr); - HString remoteHostName; - hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - return qt_QStringFromHString(remoteHostName); + if (remoteHost) { + HString remoteHostName; + hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + return qt_QStringFromHString(remoteHostName); + } + return {}; } QBluetoothAddress QBluetoothSocketPrivateWinRT::peerAddress() const @@ -645,10 +662,13 @@ QBluetoothAddress QBluetoothSocketPrivateWinRT::peerAddress() const ComPtr<IHostName> remoteHost; hr = info->get_RemoteAddress(&remoteHost); Q_ASSERT_SUCCEEDED(hr); - HString remoteAddress; - hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - return QBluetoothAddress(qt_QStringFromHString(remoteAddress)); + if (remoteHost) { + HString remoteAddress; + hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + return QBluetoothAddress(fromWinApiAddress(std::move(remoteAddress))); + } + return QBluetoothAddress(); } quint16 QBluetoothSocketPrivateWinRT::peerPort() const @@ -661,7 +681,7 @@ quint16 QBluetoothSocketPrivateWinRT::peerPort() const hr = m_socketObject->get_Information(&info); Q_ASSERT_SUCCEEDED(hr); HString remotePortString; - hr = info->get_LocalPort(remotePortString.GetAddressOf()); + hr = info->get_RemotePort(remotePortString.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); bool ok = true; const uint port = qt_QStringFromHString(remotePortString).toUInt(&ok); diff --git a/src/bluetooth/qlowenergycontroller_darwin.mm b/src/bluetooth/qlowenergycontroller_darwin.mm index e2b3d618..019cabf8 100644 --- a/src/bluetooth/qlowenergycontroller_darwin.mm +++ b/src/bluetooth/qlowenergycontroller_darwin.mm @@ -165,7 +165,14 @@ bool QLowEnergyControllerPrivateDarwin::isValid() const void QLowEnergyControllerPrivateDarwin::init() { - using OSXBluetooth::LECBManagerNotifier; + using namespace OSXBluetooth; + + if (qt_appNeedsBluetoothUsageDescription() && !qt_appPlistContainsDescription(bluetoothUsageKey)) { + qCWarning(QT_BT_OSX) + << "The Info.plist file is required to contain " + "'NSBluetoothAlwaysUsageDescription' entry"; + return; + } QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier); if (role == QLowEnergyController::PeripheralRole) { @@ -202,7 +209,7 @@ void QLowEnergyControllerPrivateDarwin::connectToDevice() Q_FUNC_INFO, "invalid state"); if (!isValid()) { - // init() had failed for was never called. + // init() had failed or was never called. return _q_CBManagerError(QLowEnergyController::UnknownError); } @@ -234,6 +241,8 @@ void QLowEnergyControllerPrivateDarwin::connectToDevice() void QLowEnergyControllerPrivateDarwin::disconnectFromDevice() { + Q_ASSERT(isValid()); // Check for proper state is in q's code. + if (role == QLowEnergyController::PeripheralRole) { // CoreBluetooth API intentionally does not provide any way of closing // a connection. All we can do here is to stop the advertisement. @@ -241,29 +250,28 @@ void QLowEnergyControllerPrivateDarwin::disconnectFromDevice() return; } - if (isValid()) { - const auto oldState = state; + const auto oldState = state; - if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { - setState(QLowEnergyController::ClosingState); - invalidateServices(); - auto manager = centralManager.getAs<ObjCCentralManager>(); - dispatch_async(leQueue, ^{ - [manager disconnectFromDevice]; - }); + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + setState(QLowEnergyController::ClosingState); + invalidateServices(); - if (oldState == QLowEnergyController::ConnectingState) { - // With a pending connect attempt there is no - // guarantee we'll ever have didDisconnect callback, - // set the state here and now to make sure we still - // can connect. - setState(QLowEnergyController::UnconnectedState); - } - } else { - qCCritical(QT_BT_OSX) << "qt LE queue is nil, " - "can not dispatch 'disconnect'"; + auto manager = centralManager.getAs<ObjCCentralManager>(); + dispatch_async(leQueue, ^{ + [manager disconnectFromDevice]; + }); + + if (oldState == QLowEnergyController::ConnectingState) { + // With a pending connect attempt there is no + // guarantee we'll ever have didDisconnect callback, + // set the state here and now to make sure we still + // can connect. + setState(QLowEnergyController::UnconnectedState); } + } else { + qCCritical(QT_BT_OSX) << "qt LE queue is nil, " + "can not dispatch 'disconnect'"; } } @@ -274,6 +282,8 @@ void QLowEnergyControllerPrivateDarwin::discoverServices() Q_ASSERT_X(role != QLowEnergyController::PeripheralRole, Q_FUNC_INFO, "invalid role (peripheral)"); + Q_ASSERT(isValid()); // Check we're in a proper state is in q's code. + dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue()); Q_ASSERT_X(leQueue, Q_FUNC_INFO, "LE queue not found"); @@ -334,6 +344,11 @@ QLowEnergyService * QLowEnergyControllerPrivateDarwin::addServiceHelper(const QL Q_UNUSED(service); qCDebug(QT_BT_OSX, "peripheral role is not supported on tvOS"); #else + if (!isValid()) { + qCWarning(QT_BT_OSX) << "invalid peripheral"; + return nullptr; + } + if (role != QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "not in peripheral role"; return nullptr; @@ -1040,14 +1055,16 @@ void QLowEnergyControllerPrivateDarwin::startAdvertising(const QLowEnergyAdverti qCWarning(QT_BT_OSX) << "advertising is not supported on your platform"; #else - if (!isValid()) - return _q_CBManagerError(QLowEnergyController::UnknownError); - if (role != QLowEnergyController::PeripheralRole) { qCWarning(QT_BT_OSX) << "controller is not a peripheral, cannot start advertising"; return; } + if (!isValid()) { + qCWarning(QT_BT_OSX, "LE controller is an invalid peripheral"); + return; + } + if (state != QLowEnergyController::UnconnectedState) { qCWarning(QT_BT_OSX) << "invalid state" << state; return; diff --git a/src/bluetooth/qlowenergycontroller_darwin_p.h b/src/bluetooth/qlowenergycontroller_darwin_p.h index 960d7fbc..3c98ca34 100644 --- a/src/bluetooth/qlowenergycontroller_darwin_p.h +++ b/src/bluetooth/qlowenergycontroller_darwin_p.h @@ -108,7 +108,10 @@ public: const QLowEnergyAdvertisingData &scanResponseData) override; void stopAdvertising()override; QLowEnergyService *addServiceHelper(const QLowEnergyServiceData &service) override; - bool isValid() const; // QT6 - delete this logic. + + // Valid - a central or peripheral instance was allocated, and this may also + // mean a proper usage description was provided/found: + bool isValid() const; private Q_SLOTS: void _q_connected(); diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp index 5b14a92c..f69a0d91 100644 --- a/src/bluetooth/qlowenergycontroller_winrt.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -642,10 +642,10 @@ void QLowEnergyControllerPrivateWinRT::discoverServiceDetails(const QBluetoothUu QThread *thread = new QThread; worker->moveToThread(thread); connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); connect(thread, &QThread::finished, worker, &QObject::deleteLater); - connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained, - [this, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList + connect(worker, &QObject::destroyed, thread, &QObject::deleteLater); + connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained, this, + [this](const QBluetoothUuid &service, QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList , QVector<QBluetoothUuid> indicateChars , QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) { if (!serviceList.contains(service)) { @@ -668,7 +668,6 @@ void QLowEnergyControllerPrivateWinRT::discoverServiceDetails(const QBluetoothUu Q_ASSERT_SUCCEEDED(hr); pointer->setState(QLowEnergyService::ServiceDiscovered); - thread->exit(0); }); thread->start(); } diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index a6371c0a..42f4380e 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -711,8 +711,8 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() connect(this, &QLowEnergyControllerPrivateWinRTNew::abortConnection, worker, &QWinRTLowEnergyConnectionHandler::handleDeviceDisconnectRequest); connect(thread, &QThread::started, worker, &QWinRTLowEnergyConnectionHandler::connectToDevice); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); connect(thread, &QThread::finished, worker, &QObject::deleteLater); + connect(worker, &QObject::destroyed, thread, &QObject::deleteLater); connect(worker, &QWinRTLowEnergyConnectionHandler::errorOccurred, this, [this](const QString &msg) { handleConnectionError(msg.toUtf8().constData()); }); connect(worker, &QWinRTLowEnergyConnectionHandler::deviceConnected, this, @@ -1166,12 +1166,12 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot QThread *thread = new QThread; worker->moveToThread(thread); connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandlerNew::obtainCharList); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); connect(thread, &QThread::finished, worker, &QObject::deleteLater); + connect(worker, &QObject::destroyed, thread, &QObject::deleteLater); connect(worker, &QWinRTLowEnergyServiceHandlerNew::errorOccured, this, &QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError); - connect(worker, &QWinRTLowEnergyServiceHandlerNew::charListObtained, - [this, reactOnDiscoveryError, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle, + connect(worker, &QWinRTLowEnergyServiceHandlerNew::charListObtained, this, + [this, reactOnDiscoveryError](const QBluetoothUuid &service, QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList, QVector<QBluetoothUuid> indicateChars, QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) { if (!serviceList.contains(service)) { @@ -1194,12 +1194,10 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot if (FAILED(hr)) { reactOnDiscoveryError(pointer, QStringLiteral("Could not register for value changes in Xaml thread: %1").arg(hr)); - thread->exit(0); return; } pointer->setState(QLowEnergyService::ServiceDiscovered); - thread->exit(0); }); thread->start(); } |