summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2023-01-03 10:37:21 +0100
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2023-02-24 13:59:28 +0100
commitae1a1f5efce291f613a13757ff6f744fcca2d2ce (patch)
tree4d0b562d220783e75d4aa2cc3c5cae1d28fcd4e0 /src
parent935ec099758487b57970732f0ecfe7021b655968 (diff)
QtBluetooth: port the code to the new permissions API (CoreBluetooth)
We only _check_ if permissions were granted, leaving it up to an application to _request_ permissions. If permission status is not 'Granted', we bail out ealy setting MissionPermissionsError. QLowEnergyController::init is now a no-op for Darwin, because it's OK to create a peripheral/central and no need to set any error yet. Autotests require minor adjustments - they were already passing if BT is off, as it is on CI (checking Bluetooth local device and its status), but if BT is somewhere on, tests can try to scan or connect not having permissions granted - for this we adjust the tests, using permission API. [ChangeLog][Important Behavior Changes][QtBluetooth][Darwin] Do not request permissions implicitly, only check them and leave it to applications to request permissions explicitly. Task-number: QTBUG-109964 Change-Id: I95c04744e979614ffb6d992da2e279e86b272679 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Juha Vuolle <juha.vuolle@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/bluetooth/darwin/btutility.mm18
-rw-r--r--src/bluetooth/darwin/btutility_p.h6
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm23
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_macos.mm2
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin.mm66
-rw-r--r--src/bluetooth/qlowenergycontroller_darwin_p.h1
6 files changed, 48 insertions, 68 deletions
diff --git a/src/bluetooth/darwin/btutility.mm b/src/bluetooth/darwin/btutility.mm
index 706835da..e9f2156f 100644
--- a/src/bluetooth/darwin/btutility.mm
+++ b/src/bluetooth/darwin/btutility.mm
@@ -33,8 +33,6 @@ const int maxValueLength = 512;
const int defaultMtu = 23;
-NSString *const bluetoothUsageKey = @"NSBluetoothAlwaysUsageDescription";
-
QString qt_address(NSString *address)
{
if (address && address.length) {
@@ -293,22 +291,6 @@ 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/darwin/btutility_p.h b/src/bluetooth/darwin/btutility_p.h
index 193a24f7..87f4a719 100644
--- a/src/bluetooth/darwin/btutility_p.h
+++ b/src/bluetooth/darwin/btutility_p.h
@@ -123,12 +123,6 @@ extern const int defaultLEScanTimeoutMS;
extern const int maxValueLength;
extern const int defaultMtu;
-// Add more keys if needed, for now this one is enough:
-extern NSString *const bluetoothUsageKey;
-
-bool qt_appNeedsBluetoothUsageDescription();
-bool qt_appPlistContainsDescription(NSString *key);
-
} // namespace DarwinBluetooth
Q_DECLARE_LOGGING_CATEGORY(QT_BT_DARWIN)
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm
index b04e3876..0f32898b 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_darwin.mm
@@ -25,6 +25,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpermissions.h>
#include <QtCore/qvector.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
@@ -135,17 +136,17 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
// 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_DARWIN)
- << "A proper Info.plist with NSBluetoothAlwaysUsageDescription "
- "entry is required, cannot start device discovery";
- setError(QBluetoothDeviceDiscoveryAgent::MissingPermissionsError);
- emit q_ptr->errorOccurred(lastError);
- return;
+ if (methods.testFlag(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)) {
+ const auto permissionStatus = qApp->checkPermission(QBluetoothPermission{});
+ if (permissionStatus != Qt::PermissionStatus::Granted) {
+ qCWarning(QT_BT_DARWIN,
+ "Use of Bluetooth LE requires explicitly requested permissions.");
+ setError(QBluetoothDeviceDiscoveryAgent::MissingPermissionsError);
+ emit q_ptr->errorOccurred(lastError);
+ // Arguably, Classic scan is still possible, but let's keep the logic
+ // simple.
+ return;
+ }
}
requestedMethods = methods;
diff --git a/src/bluetooth/qbluetoothlocaldevice_macos.mm b/src/bluetooth/qbluetoothlocaldevice_macos.mm
index 7063b357..3d1da5ea 100644
--- a/src/bluetooth/qbluetoothlocaldevice_macos.mm
+++ b/src/bluetooth/qbluetoothlocaldevice_macos.mm
@@ -415,7 +415,7 @@ QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
QBluetoothLocalDevice defaultAdapter;
if (!defaultAdapter.isValid() || defaultAdapter.address().isNull()) {
- qCCritical(QT_BT_DARWIN) << Q_FUNC_INFO <<"no valid device found";
+ qCWarning(QT_BT_DARWIN) << Q_FUNC_INFO <<"no valid device found";
return localDevices;
}
diff --git a/src/bluetooth/qlowenergycontroller_darwin.mm b/src/bluetooth/qlowenergycontroller_darwin.mm
index 8b2646cf..3f7fb215 100644
--- a/src/bluetooth/qlowenergycontroller_darwin.mm
+++ b/src/bluetooth/qlowenergycontroller_darwin.mm
@@ -21,6 +21,8 @@
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpermissions.h>
QT_BEGIN_NAMESPACE
@@ -113,37 +115,44 @@ bool QLowEnergyControllerPrivateDarwin::isValid() const
void QLowEnergyControllerPrivateDarwin::init()
{
+ // We have to override the 'init', it's pure virtual in the base.
+ // Just creating a central or peripheral should not trigger any
+ // error yet.
+}
+
+bool QLowEnergyControllerPrivateDarwin::lazyInit()
+{
using namespace DarwinBluetooth;
- if (qt_appNeedsBluetoothUsageDescription() && !qt_appPlistContainsDescription(bluetoothUsageKey)) {
- qCWarning(QT_BT_DARWIN)
- << "The Info.plist file is required to contain "
- "'NSBluetoothAlwaysUsageDescription' entry";
- return;
+ if (peripheralManager || centralManager)
+ return true;
+
+ if (qApp->checkPermission(QBluetoothPermission{}) != Qt::PermissionStatus::Granted) {
+ qCWarning(QT_BT_DARWIN,
+ "Use of Bluetooth LE must be explicitly requested by the application.");
+ setError(QLowEnergyController::MissingPermissionsError);
+ return false;
}
std::unique_ptr<LECBManagerNotifier> notifier = std::make_unique<LECBManagerNotifier>();
if (role == QLowEnergyController::PeripheralRole) {
peripheralManager.reset([[DarwinBTPeripheralManager alloc] initWith:notifier.get()],
DarwinBluetooth::RetainPolicy::noInitialRetain);
- if (!peripheralManager) {
- qCWarning(QT_BT_DARWIN) << "failed to create a peripheral manager";
- return;
- }
+ Q_ASSERT(peripheralManager);
} else {
centralManager.reset([[DarwinBTCentralManager alloc] initWith:notifier.get()],
DarwinBluetooth::RetainPolicy::noInitialRetain);
- if (!centralManager) {
- qCWarning(QT_BT_DARWIN) << "failed to initialize a central manager";
- return;
- }
+ Q_ASSERT(centralManager);
}
+ // FIXME: Q_UNLIKELY
if (!connectSlots(notifier.get()))
qCWarning(QT_BT_DARWIN) << "failed to connect to notifier's signal(s)";
// Ownership was taken by central manager.
notifier.release();
+
+ return true;
}
void QLowEnergyControllerPrivateDarwin::connectToDevice()
@@ -151,24 +160,14 @@ void QLowEnergyControllerPrivateDarwin::connectToDevice()
Q_ASSERT_X(state == QLowEnergyController::UnconnectedState,
Q_FUNC_INFO, "invalid state");
- if (qt_appNeedsBluetoothUsageDescription()
- && !qt_appPlistContainsDescription(bluetoothUsageKey)) {
- qCWarning(QT_BT_DARWIN)
- << "The Info.plist file is required to contain "
- "'NSBluetoothAlwaysUsageDescription' entry";
- return _q_CBManagerError(QLowEnergyController::MissingPermissionsError);
- }
-
- if (!isValid()) {
- // init() had failed or was never called.
- return _q_CBManagerError(QLowEnergyController::UnknownError);
- }
-
if (deviceUuid.isNull()) {
// Wrong constructor was used or invalid UUID was provided.
return _q_CBManagerError(QLowEnergyController::UnknownRemoteDeviceError);
}
+ if (!lazyInit()) // MissingPermissionsError was emit.
+ return;
+
// The logic enforcing the role is in the public class.
Q_ASSERT_X(role != QLowEnergyController::PeripheralRole,
Q_FUNC_INFO, "invalid role (peripheral)");
@@ -188,13 +187,12 @@ void QLowEnergyControllerPrivateDarwin::connectToDevice()
void QLowEnergyControllerPrivateDarwin::disconnectFromDevice()
{
- Q_ASSERT(isValid()); // Check for proper state is in q's code.
+ Q_ASSERT(isValid()); // Check for proper state is in Qt'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.
- stopAdvertising();
- return;
+ return stopAdvertising();
}
const auto oldState = state;
@@ -287,7 +285,13 @@ void QLowEnergyControllerPrivateDarwin::addToGenericAttributeList(const QLowEner
int QLowEnergyControllerPrivateDarwin::mtu() const
{
+ // FIXME: check the state - neither public class does,
+ // nor us - not fun! E.g. readRssi correctly checked/asserted.
+
__block int mtu = DarwinBluetooth::defaultMtu;
+ if (!isValid()) // A minimal check.
+ return defaultMtu;
+
if (const auto leQueue = DarwinBluetooth::qt_LE_queue()) {
const auto *manager = centralManager.getAs<DarwinBTCentralManager>();
dispatch_sync(leQueue, ^{
@@ -1001,10 +1005,8 @@ void QLowEnergyControllerPrivateDarwin::startAdvertising(const QLowEnergyAdverti
return;
}
- if (!isValid()) {
- qCWarning(QT_BT_DARWIN, "LE controller is an invalid peripheral");
+ if (!lazyInit()) // Error was emit already.
return;
- }
if (state != QLowEnergyController::UnconnectedState) {
qCWarning(QT_BT_DARWIN) << "invalid state" << state;
diff --git a/src/bluetooth/qlowenergycontroller_darwin_p.h b/src/bluetooth/qlowenergycontroller_darwin_p.h
index d156d785..2782ff59 100644
--- a/src/bluetooth/qlowenergycontroller_darwin_p.h
+++ b/src/bluetooth/qlowenergycontroller_darwin_p.h
@@ -43,6 +43,7 @@ public:
~QLowEnergyControllerPrivateDarwin();
void init() override;
+ bool lazyInit();
void connectToDevice() override;
void disconnectFromDevice() override;
void discoverServices() override;