From afd7460affa17b5f8aac9034b1b9c3b13dd115f7 Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Wed, 3 Mar 2021 00:59:29 +0200 Subject: Add new app permissions API under QCoreApplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The API allows users to request and check the status of various permissions. A predefined enum class of the common permission types on different platforms is used to allow requesting permission with a common code. Platform specific permissions are defined only on their relevant platform. For permissions that are not predefined, they can be requested via a string variant of this API. This adds the Android implementation only. [ChangeLog][QtCore] Add new API for handling app permissions with an initial implementation for Android. Task-number: QTBUG-90498 Change-Id: I3bc98c6ab2dceeea3ee8edec20a332ed8f56ad4f Reviewed-by: Sona Kurazyan Reviewed-by: Edward Welbourne Reviewed-by: Qt CI Bot Reviewed-by: Tor Arne Vestbø --- src/corelib/CMakeLists.txt | 2 + .../doc/snippets/permissions/permissions.cpp | 141 +++++++++ src/corelib/doc/src/external-resources.qdoc | 5 + src/corelib/kernel/qcoreapplication.cpp | 151 ++++++++++ src/corelib/kernel/qcoreapplication.h | 12 +- src/corelib/kernel/qcoreapplication_android.cpp | 319 +++++++++++++++++++++ src/corelib/kernel/qcoreapplication_p.h | 14 +- src/corelib/kernel/qjnihelpers.cpp | 6 +- src/corelib/kernel/qjnihelpers_p.h | 3 +- src/corelib/kernel/qpermission.h | 78 +++++ src/corelib/kernel/qpermission.qdoc | 100 +++++++ 11 files changed, 827 insertions(+), 4 deletions(-) create mode 100644 src/corelib/doc/snippets/permissions/permissions.cpp create mode 100644 src/corelib/kernel/qcoreapplication_android.cpp create mode 100644 src/corelib/kernel/qpermission.h create mode 100644 src/corelib/kernel/qpermission.qdoc (limited to 'src/corelib') diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 8351ec9263..aeced4a588 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -119,6 +119,7 @@ qt_internal_add_module(Core kernel/qobjectcleanuphandler.cpp kernel/qobjectcleanuphandler.h kernel/qobjectdefs.h kernel/qobjectdefs_impl.h + kernel/qpermission.h kernel/qpointer.cpp kernel/qpointer.h kernel/qproperty.cpp kernel/qproperty.h kernel/qproperty_p.h kernel/qpropertyprivate.h @@ -976,6 +977,7 @@ qt_internal_extend_target(Core CONDITION UNIX AND NOT APPLE qt_internal_extend_target(Core CONDITION ANDROID AND NOT ANDROID_EMBEDDED SOURCES io/qstandardpaths_android.cpp + kernel/qcoreapplication_android.cpp io/qstorageinfo_unix.cpp kernel/qjni.cpp kernel/qjni_p.h kernel/qjnienvironment.cpp kernel/qjnienvironment.h diff --git a/src/corelib/doc/snippets/permissions/permissions.cpp b/src/corelib/doc/snippets/permissions/permissions.cpp new file mode 100644 index 0000000000..2eac419226 --- /dev/null +++ b/src/corelib/doc/snippets/permissions/permissions.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +void takeSelfie() {}; + +void requestCameraPermissionAndroid() +{ +//! [Request camera permission on Android] + QCoreApplication::requestPermission(QStringLiteral("android.permission.CAMERA")) + .then([=](QPermission::PermissionResult result) { + if (result == QPermission::Authorized) + takeSelfie(); + }); +//! [Request camera permission on Android] +} + +void requestCameraPermission() +{ +//! [Request camera permission] + QCoreApplication::requestPermission(QPermission::Camera) + .then([=](QPermission::PermissionResult result) { + if (result == QPermission::Authorized) + takeSelfie(); + }); +//! [Request camera permission] +} + +void requestCameraPermissionSyncAndroid() +{ +//! [Request camera permission sync on Android] + auto future = QCoreApplication::requestPermission(QStringLiteral("android.permission.CAMERA")); + auto result = future.result(); // blocks and waits for the result to be ready + if (result == QPermission::Authorized) + takeSelfie(); +//! [Request camera permission sync on Android] +} + +void requestCameraPermissionSync() +{ +//! [Request camera permission sync] + auto future = QCoreApplication::requestPermission(QPermission::Camera); + auto result = future.result(); // blocks and waits for the result to be ready + if (result == QPermission::Authorized) + takeSelfie(); +//! [Request camera permission sync] +} + +void checkCameraPermissionAndroid() +{ +//! [Check camera permission on Android] + QCoreApplication::checkPermission(QStringLiteral("android.permission.CAMERA")) + .then([=](QPermission::PermissionResult result) { + if (result == QPermission::Authorized) + takeSelfie(); + }); +//! [Check camera permission on Android] +} + +void checkCameraPermission() +{ +//! [Check camera permission] + QCoreApplication::checkPermission(QPermission::Camera) + .then([=](QPermission::PermissionResult result) { + if (result == QPermission::Authorized) + takeSelfie(); + }); +//! [Check camera permission] +} + +void checkCameraPermissionAndroidSync() +{ +//! [Check camera permission sync on Android] + auto future = QCoreApplication::checkPermission(QStringLiteral("android.permission.CAMERA")); + // may block and wait for the result to be ready on some platforms + auto result = future.result(); + if (result == QPermission::Authorized) + takeSelfie(); +//! [Check camera permission sync on Android] +} + +void checkCameraPermissionSync() +{ +//! [Check camera permission sync] + auto future = QCoreApplication::checkPermission(QPermission::Camera); + // may block and wait for the result to be ready on some platforms + auto result = future.result(); + if (result == QPermission::Authorized) + takeSelfie(); +//! [Check camera permission sync] +} diff --git a/src/corelib/doc/src/external-resources.qdoc b/src/corelib/doc/src/external-resources.qdoc index abd359ac33..e8f1d3348d 100644 --- a/src/corelib/doc/src/external-resources.qdoc +++ b/src/corelib/doc/src/external-resources.qdoc @@ -100,3 +100,8 @@ \externalpage https://docs.oracle.com/en/java/javase/13/docs/specs/jni/functions.html#interface-function-table \title Java: Interface Function Table */ + +/*! + \externalpage https://doc.qt.io/qtcreator/creator-deploying-android.html#editing-manifest-files + \title Qt Creator: Editing Manifest Files +*/ diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 947d61a194..676614f6d6 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -60,6 +60,7 @@ #ifndef QT_NO_QOBJECT #include #include +#include #include #if QT_CONFIG(thread) #include @@ -3076,6 +3077,156 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc \sa Q_OBJECT, QObject::tr() */ +#ifndef QT_NO_QOBJECT +#if !defined(Q_OS_ANDROID) + + QFuture defaultPermissionFuture() + { + QPromise promise; + QFuture future = promise.future(); + promise.start(); + #ifndef QT_NO_EXCEPTIONS + const auto exception = std::make_exception_ptr( + std::runtime_error("This platform doesn't have an implementation " + "for the application permissions API.")); + promise.setException(exception); + #else + promise.addResult(QPermission::Denied); + #endif + promise.finish(); + return future; + } + + QFuture + QCoreApplicationPrivate::requestPermission(QPermission::PermisionType permission) + { + Q_UNUSED(permission) + return defaultPermissionFuture(); + } + + QFuture + QCoreApplicationPrivate::requestPermission(const QString &permission) + { + Q_UNUSED(permission) + return defaultPermissionFuture(); + } + + QFuture + QCoreApplicationPrivate::checkPermission(QPermission::PermisionType permission) + { + Q_UNUSED(permission) + return defaultPermissionFuture(); + } + + QFuture + QCoreApplicationPrivate::checkPermission(const QString &permission) + { + Q_UNUSED(permission) + return defaultPermissionFuture(); + } +#endif + +/*! + Requests the \a permission and returns a QFuture representing the + result of the request. + + Applications can request a permission in a cross-platform fashion. For example + you can request permission to use the camera asynchronously as follows: + + \snippet permissions/permissions.cpp Request camera permission + + \note A function passed to \l {QFuture::then()} will be called once the request + is processed. It can take some suitable action in response to the + granting or refusal of the permission. It must not access objects that + might be deleted before it is called. + + To do the same request synchronously: + + \snippet permissions/permissions.cpp Request camera permission sync + + \since 6.2 + \sa checkPermission() +*/ +QFuture +QCoreApplication::requestPermission(QPermission::PermisionType permission) +{ + return QCoreApplicationPrivate::requestPermission(permission); +} + +/*! + Requests the \a permission and returns a QFuture representing the + result of the request. + + All application permissions supported by a platform can be requested by their + platform-specific names. For example you can request permission to use the + camera asynchronously on Android as follows: + + \snippet permissions/permissions.cpp Request camera permission on Android + + \note A function passed to \l {QFuture::then()} will be called once the request + is processed. It can take some suitable action in response to the + granting or refusal of the permission. It must not access objects that + might be deleted before it is called. + + To do the same request synchronously: + + \snippet permissions/permissions.cpp Request camera permission sync on Android + + \since 6.2 + \sa checkPermission() +*/ +QFuture +QCoreApplication::requestPermission(const QString &permission) +{ + return QCoreApplicationPrivate::requestPermission(permission); +} + +/*! + Checks whether this process has the named \a permission and returns a QFuture + representing the result of the check. + + Applications can check a permission in a cross-platform fashion. For example + you can check the permission to use the camera asynchronously as follows: + + \snippet permissions/permissions.cpp Check camera permission + + To do the same request synchronously: + + \snippet permissions/permissions.cpp Check camera permission sync + + \since 6.2 + \sa requestPermission() +*/ +QFuture +QCoreApplication::checkPermission(QPermission::PermisionType permission) +{ + return QCoreApplicationPrivate::checkPermission(permission); +} + +/*! + Checks whether this process has the named \a permission and returns a QFuture + representing the result of the check. + + All application permissions supported by a platform can be checked by their + platform-specific names. For example you can check the permission to use the + camera asynchronously on Android as follows: + + \snippet permissions/permissions.cpp Check camera permission on Android + + To do the same request synchronously: + + \snippet permissions/permissions.cpp Check camera permission sync on Android + + \since 6.2 + \sa requestPermission() +*/ +QFuture +QCoreApplication::checkPermission(const QString &permission) +{ + return QCoreApplicationPrivate::checkPermission(permission); +} +#endif + QT_END_NAMESPACE #ifndef QT_NO_QOBJECT diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index f581d9b85c..1bb0612100 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -43,9 +43,11 @@ #include #include #ifndef QT_NO_QOBJECT -#include #include #include +#include +#include +#include #else #include #endif @@ -156,6 +158,14 @@ public: int n = -1); #ifndef QT_NO_QOBJECT + static QFuture requestPermission( + QPermission::PermisionType permission); + static QFuture requestPermission(const QString &permission); + + static QFuture checkPermission( + QPermission::PermisionType permission); + static QFuture checkPermission(const QString &permission); + void installNativeEventFilter(QAbstractNativeEventFilter *filterObj); void removeNativeEventFilter(QAbstractNativeEventFilter *filterObj); diff --git a/src/corelib/kernel/qcoreapplication_android.cpp b/src/corelib/kernel/qcoreapplication_android.cpp new file mode 100644 index 0000000000..096226eafb --- /dev/null +++ b/src/corelib/kernel/qcoreapplication_android.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcoreapplication.h" +#include "qcoreapplication_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char qtNativeClassName[] = "org/qtproject/qt/android/QtNative"; + +#ifndef QT_NO_EXCEPTIONS +static const char emptyPermissionExcept[] = "The permission cannot be an empty string."; +static const char invalidNativePermissionExcept[] = + "Coudn't convert the provided permission type to a native Android permission string."; +#endif + +QPermission::PermissionResult resultFromAndroid(jint value) +{ + return value == 0 ? QPermission::Authorized : QPermission::Denied; +} + +using PendingPermissionRequestsHash + = QHash>>; +Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests); +static QBasicMutex g_pendingPermissionRequestsMutex; + +static int nextRequestCode() +{ + static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0); + return counter.fetchAndAddRelaxed(1); +} + +static QStringList nativeStringsFromPermission(QPermission::PermisionType permission) +{ + static const auto precisePerm = QStringLiteral("android.permission.ACCESS_FINE_LOCATION"); + static const auto coarsePerm = QStringLiteral("android.permission.ACCESS_COARSE_LOCATION"); + static const auto backgroundPerm = + QStringLiteral("android.permission.ACCESS_BACKGROUND_LOCATION"); + + switch (permission) { + case QPermission::CoarseLocation: + return {coarsePerm}; + case QPermission::PreciseLocation: + return {precisePerm}; + case QPermission::CoarseBackgroundLocation: + // Keep the background permission first to be able to use .first() + // in checkPermission because it takes single permission + if (QtAndroidPrivate::androidSdkVersion() >= 29) + return {backgroundPerm, coarsePerm}; + return {coarsePerm}; + case QPermission::PreciseBackgroundLocation: + if (QtAndroidPrivate::androidSdkVersion() >= 29) + return {backgroundPerm, precisePerm}; + return {precisePerm}; + case QPermission::Camera: + return {QStringLiteral("android.permission.CAMERA")}; + case QPermission::Microphone: + return {QStringLiteral("android.permission.RECORD_AUDIO")}; + case QPermission::BodySensors: + return {QStringLiteral("android.permission.BODY_SENSORS")}; + case QPermission::PhysicalActivity: + return {QStringLiteral("android.permission.ACTIVITY_RECOGNITION")}; + case QPermission::ReadContacts: + return {QStringLiteral("android.permission.READ_CONTACTS")}; + case QPermission::WriteContacts: + return {QStringLiteral("android.permission.WRITE_CONTACTS")}; + case QPermission::ReadStorage: + return {QStringLiteral("android.permission.READ_EXTERNAL_STORAGE")}; + case QPermission::WriteStorage: + return {QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE")}; + case QPermission::ReadCalendar: + return {QStringLiteral("android.permission.READ_CALENDAR")}; + case QPermission::WriteCalendar: + return {QStringLiteral("android.permission.WRITE_CALENDAR")}; + + default: + return {}; + } +} + +/*! + \internal + + This function is called when the result of the permission request is available. + Once a permission is requested, the result is braodcast by the OS and listened + to by QtActivity which passes it to C++ through a native JNI method call. + */ +static void sendRequestPermissionsResult(JNIEnv *env, jobject *obj, jint requestCode, + jobjectArray permissions, jintArray grantResults) +{ + Q_UNUSED(obj); + + QMutexLocker locker(&g_pendingPermissionRequestsMutex); + auto it = g_pendingPermissionRequests->constFind(requestCode); + if (it == g_pendingPermissionRequests->constEnd()) { + qWarning() << "Found no valid pending permission request for request code" << requestCode; + return; + } + + auto request = *it; + g_pendingPermissionRequests->erase(it); + locker.unlock(); + + const int size = env->GetArrayLength(permissions); + std::unique_ptr results(new jint[size]); + env->GetIntArrayRegion(grantResults, 0, size, results.get()); + + for (int i = 0 ; i < size; ++i) { + QPermission::PermissionResult result = resultFromAndroid(results[i]); + request->addResult(result, i); + } + + request->finish(); +} + +QFuture requestPermissionsInternal(const QStringList &permissions) +{ + QSharedPointer> promise; + promise.reset(new QPromise()); + QFuture future = promise->future(); + promise->start(); + + // No mechanism to request permission for SDK version below 23, because + // permissions defined in the manifest are granted at install time. + if (QtAndroidPrivate::androidSdkVersion() < 23) { + for (int i = 0; i < permissions.size(); ++i) + promise->addResult(QCoreApplication::checkPermission(permissions.at(i)).result(), i); + promise->finish(); + return future; + } + + const int requestCode = nextRequestCode(); + QMutexLocker locker(&g_pendingPermissionRequestsMutex); + g_pendingPermissionRequests->insert(requestCode, promise); + + QtAndroidPrivate::runOnAndroidThread([permissions, requestCode] { + QJniEnvironment env; + jclass clazz = env.findClass("java/lang/String"); + auto array = env->NewObjectArray(permissions.size(), clazz, nullptr); + int index = 0; + + for (auto &perm : permissions) + env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object()); + + QJniObject(QtAndroidPrivate::activity()).callMethod("requestPermissions", + "([Ljava/lang/String;I)V", + array, + requestCode); + env->DeleteLocalRef(array); + }, QJniEnvironment().jniEnv()); + + return future; +} + +QFuture +QCoreApplicationPrivate::requestPermission(const QString &permission) +{ + // avoid the uneccessary call and response to an empty permission string + if (permission.size() > 0) + return requestPermissionsInternal({permission}); + + QPromise promise; + QFuture future = promise.future(); + promise.start(); +#ifndef QT_NO_EXCEPTIONS + promise.setException(std::make_exception_ptr(std::runtime_error(emptyPermissionExcept))); +#else + promise.addResult(QPermission::Denied); +#endif + promise.finish(); + return future; +} + +QFuture groupRequestToSingleResult(const QStringList &permissions) +{ + QSharedPointer> promise; + promise.reset(new QPromise()); + QFuture future = promise->future(); + promise->start(); + requestPermissionsInternal(permissions).then( + [promise](QFuture future) { + auto results = future.results(); + if (results.count(QPermission::Authorized) == results.count()) + promise->addResult(QPermission::Authorized, 0); + else + promise->addResult(QPermission::Denied, 0); + promise->finish(); + }); + + return future; +} + +QFuture +QCoreApplicationPrivate::requestPermission(QPermission::PermisionType permission) +{ + const auto nativePermissions = nativeStringsFromPermission(permission); + + if (nativePermissions.size() > 0) + return groupRequestToSingleResult(nativePermissions); + + QPromise promise; + QFuture future = promise.future(); + promise.start(); +#ifndef QT_NO_EXCEPTIONS + promise.setException(std::make_exception_ptr( + std::runtime_error(invalidNativePermissionExcept))); +#else + promise.addResult(QPermission::Denied); +#endif + promise.finish(); + return future; +} + +QFuture +QCoreApplicationPrivate::checkPermission(const QString &permission) +{ + QPromise promise; + QFuture future = promise.future(); + promise.start(); + + if (permission.size() > 0) { + auto res = QJniObject::callStaticMethod(qtNativeClassName, + "checkSelfPermission", + "(Ljava/lang/String;)I", + QJniObject::fromString(permission).object()); + promise.addResult(resultFromAndroid(res)); + } else { +#ifndef QT_NO_EXCEPTIONS + promise.setException(std::make_exception_ptr(std::runtime_error(emptyPermissionExcept))); +#else + promise.addResult(QPermission::Denied); +#endif + } + + promise.finish(); + return future; +} + +QFuture +QCoreApplicationPrivate::checkPermission(QPermission::PermisionType permission) +{ + const auto nativePermissions = nativeStringsFromPermission(permission); + + if (nativePermissions.size() > 0) + return checkPermission(nativePermissions.first()); + + QPromise promise; + QFuture future = promise.future(); + promise.start(); +#ifndef QT_NO_EXCEPTIONS + promise.setException(std::make_exception_ptr( + std::runtime_error(invalidNativePermissionExcept))); +#else + promise.addResult(QPermission::Denied); +#endif + promise.finish(); + return future; +} + +bool QtAndroidPrivate::registerPermissionNatives() +{ + if (QtAndroidPrivate::androidSdkVersion() < 23) + return true; + + JNINativeMethod methods[] = { + {"sendRequestPermissionsResult", "(I[Ljava/lang/String;[I)V", + reinterpret_cast(sendRequestPermissionsResult) + }}; + + QJniEnvironment env; + return env.registerNativeMethods(qtNativeClassName, methods, 1); +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index a888c4310f..54e2ac4151 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -62,6 +62,8 @@ #ifndef QT_NO_QOBJECT #include "private/qobject_p.h" #include "private/qlocking_p.h" +#include +#include #endif #ifdef Q_OS_MACOS @@ -200,6 +202,16 @@ public: QString qmljs_debug_arguments; // a string containing arguments for js/qml debugging. inline QString qmljsDebugArgumentsString() const { return qmljs_debug_arguments; } +#ifndef QT_NO_QOBJECT + static QFuture requestPermission( + QPermission::PermisionType permission); + static QFuture requestPermission(const QString &permission); + + static QFuture checkPermission( + QPermission::PermisionType permission); + static QFuture checkPermission(const QString &permission); +#endif + #ifdef QT_NO_QOBJECT QCoreApplication *q_ptr; #endif diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp index 0ad5988297..a7e368017a 100644 --- a/src/corelib/kernel/qjnihelpers.cpp +++ b/src/corelib/kernel/qjnihelpers.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -47,6 +47,7 @@ #include "qsharedpointer.h" #include "qthread.h" +#include #include #include @@ -397,6 +398,9 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) if (!regOk && QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; + if (!registerPermissionNatives()) + return JNI_ERR; + g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative, "runPendingCppRunnablesOnAndroidThread", "()V"); diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h index a0d9c219d7..2147271ecb 100644 --- a/src/corelib/kernel/qjnihelpers_p.h +++ b/src/corelib/kernel/qjnihelpers_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -129,6 +129,7 @@ namespace QtAndroidPrivate Q_CORE_EXPORT PermissionsHash requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs = INT_MAX); Q_CORE_EXPORT PermissionsResult checkPermission(const QString &permission); Q_CORE_EXPORT bool shouldShowRequestPermissionRationale(const QString &permission); + bool registerPermissionNatives(); Q_CORE_EXPORT void handleActivityResult(jint requestCode, jint resultCode, jobject data); Q_CORE_EXPORT void registerActivityResultListener(ActivityResultListener *listener); diff --git a/src/corelib/kernel/qpermission.h b/src/corelib/kernel/qpermission.h new file mode 100644 index 0000000000..b20dba8849 --- /dev/null +++ b/src/corelib/kernel/qpermission.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPERMISSION_H +#define QPERMISSION_H + +#include + +QT_BEGIN_NAMESPACE + +namespace QPermission +{ + +enum PermisionType { + Camera, + Microphone, + Bluetooth, + CoarseLocation, + PreciseLocation, + CoarseBackgroundLocation, + PreciseBackgroundLocation, + BodySensors, + PhysicalActivity, + ReadContacts, + WriteContacts, + ReadStorage, + WriteStorage, + ReadCalendar, + WriteCalendar +}; + +enum PermissionResult { + Authorized, + Denied, + Restricted, + Undetermined +}; +} // QPermission + +QT_END_NAMESPACE + +#endif // QPERMISSION_H diff --git a/src/corelib/kernel/qpermission.qdoc b/src/corelib/kernel/qpermission.qdoc new file mode 100644 index 0000000000..02fd377d64 --- /dev/null +++ b/src/corelib/kernel/qpermission.qdoc @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \namespace QPermission + + \brief The QPermission namespace contains enums for app permission types and results. + + This namespace's enums are used by \l {QCoreApplication::requestPermission()} and + \l {QCoreApplication::checkPermission()}. +*/ + +/*! + \enum QPermission::PermisionType + + Predefined sets of permission values. + + \value Camera Access the camera for taking pictures or videos and configuring + the camera settings. Maps to "android.permission.CAMERA" on Android. + \value Microphone Access the microphone, receive the sound signal (e.g. to + analyze or record it). Maps to \c "android.permission.RECORD_AUDIO" + on Android. + \value CoarseLocation Access approximate location provider (wifi, cell tower). + Maps to \c "android.permission.ACCESS_COARSE_LOCATION" on Android. + \value PreciseLocation Location Access accurate location provider (satellite). + Maps to \c "android.permission.ACCESS_FINE_LOCATION" on Android. + \value PreciseBackgroundLocation Access the precise location services when + the app is in the background. Maps to + \c "android.permission.ACCESS_BACKGROUND_LOCATION" on Android, and + implies \l PreciseLocation. + \value CoarseBackgroundLocation Access the approximate location services when + the app is in the background. Maps to + \c "android.permission.ACCESS_BACKGROUND_LOCATION" on Android, and + implies \l CoarseLocation. + \value BodySensors Access body sensors such as a heart rate sensor. + Maps to \c "android.permission.BODY_SENSORS" on Android. + \value PhysicalActivity Access to data about physical activity and body + movements. Maps to \c "android.permission.ACTIVITY_RECOGNITION" + on Android. + \value ReadContacts Read user contacts. Maps to + \c "android.permission.READ_CONTACTS" on Android. + \value WriteContacts Write user contacts. Maps to + \c "android.permission.WRITE_CONTACTS" on Android. + \value ReadStorage Access device storage with read permissions. + Maps to \c "android.permission.READ_EXTERNAL_STORAGE" on Android. + \value WriteStorage Access device storage with write permissions. + Maps to \c "android.permission.WRITE_EXTERNAL_STORAGE" on Android. + \value ReadCalendar Read the user's calendar. + Maps to \c "android.permission.READ_CALENDAR" on Android. + \value WriteCalendar Write to the user's calendar. + Maps to \c "android.permission.WRITE_CALENDAR" on Android. + + \note Both Android and iOS require the native permission values to be added + to the \c AndroidManifest.xml and \c info.plist respectively. For more + information on Android permissions, see \c {Qt Creator: Editing Manifest Files}. + For more information on iOS \c info.plist, see + \l {Information Property List Files}. + + \since 6.2 + \sa QCoreApplication::requestPermission(), QCoreApplication::checkPermission() +*/ + +/*! + \enum QPermission::PermissionResult + + The result values for a permission check or request. + + \value Authorized The permission is authorized. + \value Denied The permission is denied. + \value Restricted The permission state is denied and cannot be changed due + to restrictions from the system. + \value Undetermined The permission state is not yet known. + + \since 6.2 + \sa QCoreApplication::requestPermission(), QCoreApplication::checkPermission() +*/ -- cgit v1.2.3