summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuha Vuolle <juha.vuolle@insta.fi>2022-02-25 11:15:03 +0200
committerJuha Vuolle <juha.vuolle@insta.fi>2022-03-24 12:54:56 +0200
commit542984380435ff254e927cc6fff1e3b3bd8adb04 (patch)
tree6737b0fcbd6141f5215669ee110a5173b32ccb4f
parent438f9ca628e03461102cf757230ee735c0feb0d3 (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.pri6
-rw-r--r--src/bluetooth/android/androidutils.cpp89
-rw-r--r--src/bluetooth/android/androidutils_p.h71
-rw-r--r--src/bluetooth/bluetooth.pro5
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp10
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_android.cpp17
-rw-r--r--src/bluetooth/qbluetoothsocket_android.cpp5
-rw-r--r--src/bluetooth/qlowenergycontroller_android.cpp12
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);