summaryrefslogtreecommitdiffstats
path: root/src/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth')
-rw-r--r--src/bluetooth/osx/osxbtconnectionmonitor.mm24
-rw-r--r--src/bluetooth/osx/osxbtconnectionmonitor_p.h2
-rw-r--r--src/bluetooth/osx/osxbtutility.mm19
-rw-r--r--src/bluetooth/osx/osxbtutility_p.h6
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm21
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_osx.mm6
-rw-r--r--src/bluetooth/qbluetoothsocket_winrt.cpp46
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin.mm65
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin_p.h5
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt.cpp7
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt_new.cpp10
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();
}