diff options
author | Juha Vuolle <juha.vuolle@insta.fi> | 2022-02-25 11:15:03 +0200 |
---|---|---|
committer | Juha Vuolle <juha.vuolle@insta.fi> | 2022-03-24 12:54:56 +0200 |
commit | 542984380435ff254e927cc6fff1e3b3bd8adb04 (patch) | |
tree | 6737b0fcbd6141f5215669ee110a5173b32ccb4f | |
parent | 438f9ca628e03461102cf757230ee735c0feb0d3 (diff) |
Introduce Android 12 / SDK 31+ bluetooth permissions
The Android 12 introduces three new bluetooth runtime permissions
which are required to use bluetooth. This commit introduces these
permissions.
The permission requirements are sprinkled throughout
the Android bluetooth APIs, and we need to make sure all user codepaths
are covered.
As next step we can also reduce the < 31 permissions when building
for 31+ target, but this possibly requires some buildsystem related
support as well.
Task-number: QTBUG-99590
Change-Id: Iab4b7d6d9935509e669265ed6851990d49a3a7e1
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit a0542cff15d58db83a835f1a378a55a0ec117c9c)
Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r-- | src/bluetooth/android/android.pri | 6 | ||||
-rw-r--r-- | src/bluetooth/android/androidutils.cpp | 89 | ||||
-rw-r--r-- | src/bluetooth/android/androidutils_p.h | 71 | ||||
-rw-r--r-- | src/bluetooth/bluetooth.pro | 5 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp | 10 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothlocaldevice_android.cpp | 17 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothsocket_android.cpp | 5 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_android.cpp | 12 |
8 files changed, 209 insertions, 6 deletions
diff --git a/src/bluetooth/android/android.pri b/src/bluetooth/android/android.pri index af39a6d7..8ba98c74 100644 --- a/src/bluetooth/android/android.pri +++ b/src/bluetooth/android/android.pri @@ -6,7 +6,8 @@ HEADERS += \ android/localdevicebroadcastreceiver_p.h \ android/serveracceptancethread_p.h \ android/jni_android_p.h \ - android/lowenergynotificationhub_p.h + android/lowenergynotificationhub_p.h \ + android/androidutils_p.h SOURCES += \ @@ -17,4 +18,5 @@ SOURCES += \ android/androidbroadcastreceiver.cpp \ android/localdevicebroadcastreceiver.cpp \ android/serveracceptancethread.cpp \ - android/lowenergynotificationhub.cpp + android/lowenergynotificationhub.cpp \ + android/androidutils.cpp diff --git a/src/bluetooth/android/androidutils.cpp b/src/bluetooth/android/androidutils.cpp new file mode 100644 index 00000000..ec4dce8a --- /dev/null +++ b/src/bluetooth/android/androidutils.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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:COMM$ +** +** 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. +** +** $QT_END_LICENSE$ +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +****************************************************************************/ + +#include "androidutils_p.h" + +#include <QtCore/QLoggingCategory> +#include <QtAndroidExtras/QAndroidJniEnvironment> +#include <QtCore/private/qjnihelpers_p.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + +static QString androidPermissionString(BluetoothPermission permission) +{ + switch (permission) { + case BluetoothPermission::Scan: + return {QStringLiteral("android.permission.BLUETOOTH_SCAN")}; + case BluetoothPermission::Advertise: + return {QStringLiteral("android.permission.BLUETOOTH_ADVERTISE")}; + case BluetoothPermission::Connect: + return {QStringLiteral("android.permission.BLUETOOTH_CONNECT")}; + } + return {}; +} + +bool ensureAndroidPermission(BluetoothPermission permission) +{ + // The current set of permissions are only applicable with 31+ + if (QtAndroidPrivate::androidSdkVersion() < 31) + return true; + + const auto permString = androidPermissionString(permission); + + // First check if we have the permission already + if (QtAndroidPrivate::checkPermission(permString) + == QtAndroidPrivate::PermissionsResult::Granted) { + return true; + } + + // If we didn't have the permission, request it + QAndroidJniEnvironment env; + auto result = QtAndroidPrivate::requestPermissionsSync(env, QStringList() << permString); + if (result.contains(permString) + && result[permString] == QtAndroidPrivate::PermissionsResult::Granted) { + return true; + } + + qCWarning(QT_BT_ANDROID) << "Permission not authorized:" << permString; + return false; +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/android/androidutils_p.h b/src/bluetooth/android/androidutils_p.h new file mode 100644 index 00000000..2e2f6141 --- /dev/null +++ b/src/bluetooth/android/androidutils_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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:COMM$ +** +** 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. +** +** $QT_END_LICENSE$ +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +** +****************************************************************************/ + +#ifndef QANDROIDBLUETOOTHUTILS_H +#define QANDROIDBLUETOOTHUTILS_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 <qglobal.h> +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE + +enum class BluetoothPermission { + Scan, + Advertise, + Connect +}; + +// Checks if a permssion is already authorized and requests if not. +// Returns true if permission is successfully authorized +bool ensureAndroidPermission(BluetoothPermission permission); + +QT_END_NAMESPACE + +#endif // QANDROIDBLUETOOTHUTILS_H diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 9f42b34e..876685b6 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -138,7 +138,10 @@ qtConfig(bluez) { android.permission.BLUETOOTH \ android.permission.BLUETOOTH_ADMIN \ android.permission.ACCESS_FINE_LOCATION \ - android.permission.ACCESS_COARSE_LOCATION # since Android 6.0 (API lvl 23) + android.permission.ACCESS_COARSE_LOCATION \ # since Android 6.0 (API lvl 23) + android.permission.BLUETOOTH_SCAN \ # since Android 12.0 (API lvl 31) + android.permission.BLUETOOTH_ADVERTISE \ # since Android 12.0 (API lvl 31) + android.permission.BLUETOOTH_CONNECT # since Android 12.0 (API lvl 31) ANDROID_BUNDLED_JAR_DEPENDENCIES = \ jar/QtAndroidBluetooth.jar:org.qtproject.qt5.android.bluetooth.QtBluetoothBroadcastReceiver diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp index 92140ced..788b819d 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp @@ -38,6 +38,7 @@ ** ****************************************************************************/ +#include "android/androidutils_p.h" #include "qbluetoothdevicediscoveryagent_p.h" #include <QtCore/QLoggingCategory> #include <QtBluetooth/QBluetoothAddress> @@ -220,6 +221,15 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent qCDebug(QT_BT_ANDROID) << "Location turned on"; + if (!(ensureAndroidPermission(BluetoothPermission::Scan) && + ensureAndroidPermission(BluetoothPermission::Connect))) { + errorString = QBluetoothDeviceDiscoveryAgent::tr( + "Search is not possible because of missing permissions."); + lastError = QBluetoothDeviceDiscoveryAgent::UnknownError; + emit q->error(lastError); + return; + } + // install Java BroadcastReceiver if (!receiver) { // SDP based device discovery diff --git a/src/bluetooth/qbluetoothlocaldevice_android.cpp b/src/bluetooth/qbluetoothlocaldevice_android.cpp index ef7e4074..002d699f 100644 --- a/src/bluetooth/qbluetoothlocaldevice_android.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_android.cpp @@ -38,6 +38,9 @@ ** ****************************************************************************/ +#include "android/androidutils_p.h" +#include "android/localdevicebroadcastreceiver_p.h" +#include "qbluetoothlocaldevice_p.h" #include <QtCore/QLoggingCategory> #include <QtCore/private/qjnihelpers_p.h> #include <QtAndroidExtras/QAndroidJniEnvironment> @@ -45,9 +48,6 @@ #include <QtBluetooth/QBluetoothLocalDevice> #include <QtBluetooth/QBluetoothAddress> -#include "qbluetoothlocaldevice_p.h" -#include "android/localdevicebroadcastreceiver_p.h" - QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) @@ -110,6 +110,12 @@ void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) return; } + if (!(ensureAndroidPermission(BluetoothPermission::Scan) && + ensureAndroidPermission(BluetoothPermission::Connect))) { + qCWarning(QT_BT_ANDROID) << "Local device unable to get permissions."; + return; + } + obj = new QAndroidJniObject(adapter); if (!address.isNull()) { const QString localAddress @@ -308,6 +314,11 @@ QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices() { + // As a static class function we need to ensure permissions here (in addition to initialize()) + if (!ensureAndroidPermission(BluetoothPermission::Connect)) { + qCWarning(QT_BT_ANDROID) << "allDevices() unable to get permission."; + return {}; + } // Android only supports max of one device (so far) QList<QBluetoothHostInfo> localDevices; diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index 9c243563..ed1d6373 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -43,6 +43,7 @@ #include "qbluetoothaddress.h" #include "qbluetoothdeviceinfo.h" #include "qbluetoothserviceinfo.h" +#include "android/androidutils_p.h" #include <QtCore/QLoggingCategory> #include <QtCore/QThread> #include <QtCore/QTime> @@ -192,6 +193,10 @@ QBluetoothSocketPrivateAndroid::QBluetoothSocketPrivateAndroid() "()Landroid/bluetooth/BluetoothAdapter;"); qRegisterMetaType<QBluetoothSocket::SocketError>(); qRegisterMetaType<QBluetoothSocket::SocketState>(); + + // Many functions of this class need connect permission; request already in ctor + if (!ensureAndroidPermission(BluetoothPermission::Connect)) + qCWarning(QT_BT_ANDROID) << "Bluetooth socket unable to get permission."; } QBluetoothSocketPrivateAndroid::~QBluetoothSocketPrivateAndroid() diff --git a/src/bluetooth/qlowenergycontroller_android.cpp b/src/bluetooth/qlowenergycontroller_android.cpp index 289d3eda..2bb40e1b 100644 --- a/src/bluetooth/qlowenergycontroller_android.cpp +++ b/src/bluetooth/qlowenergycontroller_android.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include "android/androidutils_p.h" #include "qlowenergycontroller_android_p.h" #include <QtCore/QLoggingCategory> #include <QtAndroidExtras/QAndroidJniEnvironment> @@ -92,6 +93,11 @@ void QLowEnergyControllerPrivateAndroid::init() const bool isPeripheral = (role == QLowEnergyController::PeripheralRole); const jint version = QtAndroidPrivate::androidSdkVersion(); + if (!ensureAndroidPermission(BluetoothPermission::Connect)) { + qCWarning(QT_BT_ANDROID) << "Unable to get needed permissions"; + return; + } + if (isPeripheral) { if (version < 21) { qWarning() << "Qt Bluetooth LE Peripheral support not available" @@ -1020,6 +1026,12 @@ void QLowEnergyControllerPrivateAndroid::startAdvertising(const QLowEnergyAdvert return; } + if (!ensureAndroidPermission(BluetoothPermission::Advertise)) { + setError(QLowEnergyController::AdvertisingError); + setState(QLowEnergyController::UnconnectedState); + return; + } + // Pass on advertisingData, scanResponse & AdvertiseSettings QAndroidJniObject jAdvertiseData = createJavaAdvertiseData(advertisingData); QAndroidJniObject jScanResponse = createJavaAdvertiseData(scanResponseData); |