Revision control
++ // Copyright (C) 2022 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qpermissions.h"
++ #include "qpermissions_p.h"
++ #include "qhashfunctions.h"
++
++ #include <QtCore/qshareddata.h>
++ #include <QtCore/qdebug.h>
++
++ QT_BEGIN_NAMESPACE
++
++ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
++
++ /*!
++ \page permissions.html
++ \title Application Permissions
++ \brief Managing application permissions.
++
++ Many features of today's devices and operating systems can have
++ significant privacy, security, and performance implications if
++ misused. It's therefore increasingly common for platforms to
++ require explicit consent from the user before accessing these
++ features.
++
++ The Qt permission APIs allow the application to check or request
++ permission for such features in a cross platform manner.
++
++ \section1 Usage
++
++ A feature that commonly requires user consent is access to the
++ microphone of the device. An application for recording voice
++ memos would perhaps look something like this initially:
++
++ \code
++ void VoiceMemoWidget::onRecordingInitiated()
++ {
++ m_microphone->startRecording();
++ }
++ \endcode
++
++ To ensure this application works well on platforms that
++ require user consent for microphone access we would extend
++ it like this:
++
++ \code
++ void VoiceMemoWidget::onRecordingInitiated()
++ {
++ QMicrophonePermission microphonePermission;
++ switch (qApp->checkPermission(microphonePermission)) {
++ case Qt::PermissionStatus::Undetermined:
++ qApp->requestPermission(microphonePermission, this,
++ &VoiceMemoWidget::onRecordingInitiated);
++ return;
++ case Qt::PermissionStatus::Denied:
++ m_permissionInstructionsDialog->show();
++ return;
++ case Qt::PermissionStatus::Granted:
++ m_microphone->startRecording();
++ }
++ }
++ \endcode
++
++ We first check if we already know the status of the microphone permission.
++ If we don't we initiate a permission request to determine the current
++ status, which will potentially ask the user for consent. We connect the
++ result of the request to the slot we're already in, so that we get another
++ chance at evaluating the permission status.
++
++ Once the permission status is known, either because we had been granted or
++ denied permission at an earlier time, or after getting the result back from
++ the request we just initiated, we redirect the user to a dialog explaining
++ why we can not record voice memos at this time (if the permission was denied),
++ or proceed to using the microphone (if permission was granted).
++
++ \note On \macos and iOS permissions can currently only be requested for
++ GUI applications.
++
++ \section2 Declaring Permissions
++
++ Some platforms require that the permissions you request are declared
++ up front at build time.
++
++ \section3 Apple platforms
++ \target apple-usage-description
++
++ Each permission you request must be accompanied by a so called
++ \e {usage description} string in the application's
++ \l{Information Property List Files}{\c Info.plist}
++ file, describing why the application needs to access the given
++ permission. For example:
++
++ \badcode
++ <key>NSMicrophoneUsageDescription</key>
++ <string>The microphone is used to record voice memos.</string>
++ \endcode
++
++ The relevant usage description keys are described in the documentation
++ for each permission type.
++
++ To ensure the relevant permission backend is included with your
++ application, please \l{Information Property List Files}
++ {point the build system to your custom \c Info.plist}.
++
++ \sa {Information Property List Files}
++
++ \section3 Android
++ \target android-uses-permission
++
++ Each permission you request must be accompanied by a \c uses-permission
++ entry in the application's \c AndroidManifest.xml file. For example:
++
++ \badcode
++ <manifest ...>
++ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
++ </manifest>
++ \endcode
++
++ To ensure the relevant permission backend is included with your
++ application, please \l {QT_ANDROID_PACKAGE_SOURCE_DIR}
++ {point the build system to your custom \c AndroidManifest.xml}
++ or use \l {qt_add_android_permission}().
++
++ The relevant permission names are described in the documentation
++ for each permission type.
++
++ \note When using this API, the \c{<!-- %%INSERT_PERMISSIONS -->} tag must be present in
++ the AndroidManifest.xml. For further information on the use of this tag,
++ see \l {Qt Permissions and Features}
++
++ \sa {Qt Creator: Editing Manifest Files}
++
++ \section1 Available Permissions
++
++ The following permissions types are available:
++
++ \annotatedlist permissions
++
++ \note The available permission types cover core functionality of Qt modules
++ like Qt Multimedia and Qt Positioning, but do not encompass all platform-specific
++ permissions. Custom permission types are not currently supported.
++
++ \section1 Best Practices
++
++ To ensure the best possible user experience for the end user we recommend
++ adopting the following best practices for managing application permissions:
++
++ \list
++
++ \li Request the minimal set of permissions needed. For example, if you only
++ need access to the microphone, do \e not request camera permission just in case.
++ Use the properties of individual permission types to limit the permission scope
++ even further, for example \l{QContactsPermission::setAccessMode()}
++ to request read only access.
++
++ \li Request permissions in response to specific actions by the user. For example,
++ defer requesting microphone permission until the user presses the button to record
++ audio. Associating the permission request to a specific action gives the user a clearer
++ context of why the permission is needed. Do \e not request all needed permission on
++ startup.
++
++ \li Present extra context and explanation if needed. Sometimes the action by the user
++ is not enough context. Consider presenting an explanation-dialog after the user has
++ initiated the action, but before requesting the permission, so the user is aware of
++ what's about to happen when the system permission dialog subsequently pops up.
++
++ \li Be transparent and explicit about why permissions are needed. In explanation
++ dialogs and usage descriptions, be transparent about why the particular permission
++ is needed for your application to provide a specific feature, so users can make
++ informed decisions.
++
++ \li Account for denied permissions. The permissions you request may be denied
++ for various reasons. You should always account for this situation, by gracefully
++ degrading the experience of your application, and presenting clear explanations
++ the user about the situation.
++
++ \li Never request permissions from a library. The request of permissions should
++ be done as close as possible to the user, where the information needed to make
++ good decisions on the points above is available. Libraries can check permissions,
++ to ensure they have the prerequisites for doing their work, but if the permission
++ is undetermined or denied this should be reflected through the library's API,
++ so that the application in turn can request the necessary permissions.
++
++ \endlist
++ */
++
++
++ /*!
++ \class QPermission
++ \inmodule QtCore
++ \inheaderfile QPermissions
++ \since 6.5
++ \brief An opaque wrapper of a typed permission.
++
++ The QPermission class is an opaque wrapper of a \l{typed permission},
++ used when checking or requesting permissions. You do not need to construct
++ this type explicitly, as the type is automatically used when checking or
++ requesting permissions:
++
++ \code
++ qApp->checkPermission(QCameraPermission{});
++ \endcode
++
++ When requesting permissions, the given functor will
++ be passed an instance of a QPermission, which can be used
++ to check the result of the request:
++
++ \code
++ qApp->requestPermission(QCameraPermission{}, [](const QPermission &permission) {
++ if (permission.status() == Qt::PermissionStatus::Granted)
++ takePhoto();
++ });
++ \endcode
++
++ To inspect the properties of the original, typed permission,
++ use the \l {QPermission::}{value()} function:
++
++ \code
++ QLocationPermission locationPermission;
++ locationPermission.setAccuracy(QLocationPermission::Precise);
++ qApp->requestPermission(locationPermission, this, &LocationWidget::permissionUpdated);
++ \endcode
++
++ \code
++ void LocationWidget::permissionUpdated(const QPermission &permission)
++ {
++ if (permission.status() != Qt::PermissionStatus::Granted)
++ return;
++ auto locationPermission = permission.value<QLocationPermission>();
++ if (!locationPermission || locationPermission->accuracy() != QLocationPermission::Precise)
++ return;
++ updatePreciseLocation();
++ }
++ \endcode
++
++ \target typed permission
++ \section1 Typed Permissions
++
++ The following permissions are available:
++
++ \annotatedlist permissions
++
++ \sa {Application Permissions}
++ */
++
++ /*!
++ \fn template <typename T, QPermission::if_permission<T>> QPermission::QPermission(const T &type)
++
++ Constructs a permission from the given \l{typed permission} \a type.
++
++ You do not need to construct this type explicitly, as the type is automatically
++ used when checking or requesting permissions.
++
++ \constraints
++ \c T is one of the \l{typed permission} classes:
++
++ \annotatedlist permissions
++ */
++
++ /*!
++ \fn template <typename T, QPermission::if_permission<T>> std::optional<T> QPermission::value() const
++
++ Returns the \l{typed permission} of type \c T, or \c{std::nullopt} if this
++ QPermission object doesn't contain one.
++
++ Use type() for dynamically choosing which typed permission to request.
++
++ \constraints
++ \c T is one of the \l{typed permission} classes:
++
++ \annotatedlist permissions
++ */
++
++ /*!
++ \fn Qt::PermissionStatus QPermission::status() const
++ Returns the status of the permission.
++ */
++
++ /*!
++ \fn QMetaType QPermission::type() const
++ Returns the type of the permission.
++ */
++
++ /*
++ \internal
++ */
++ const void *QPermission::data(QMetaType requestedType) const
++ {
++ const auto actualType = type();
++ if (requestedType != actualType)
++ return nullptr;
++ return m_data.data();
++ }
++
++ // check alignof(AlignmentCheck) instead of alignof(void*), in case
++ // pointers have different alignment inside structs:
++ struct AlignmentCheck { void *p; };
++
++ #define QT_PERMISSION_IMPL_COMMON(ClassName) \
++ /* Class##Private is unused until we need it: */ \
++ static_assert(sizeof(ClassName) == sizeof(void*), \
++ "You have added too many members to " #ClassName "::ShortData. " \
++ "Decrease their size or switch to using a d-pointer."); \
++ static_assert(alignof(ClassName) == alignof(AlignmentCheck), \
++ "You have added members to " #ClassName "::ShortData that are overaligned. " \
++ "Decrease their alignment or switch to using a d-pointer."); \
++ ClassName::ClassName(const ClassName &other) noexcept = default; \
++ ClassName::~ClassName() = default; \
++ ClassName &ClassName::operator=(const ClassName &other) noexcept = default; \
++ ClassName::ClassName() \
++ /* impl supplied by caller */
++
++
++ /*!
++ \class QCameraPermission
++ \brief Access the camera for taking pictures or videos.
++
++ \section1 Requirements
++
++ \include permissions.qdocinc begin-usage-declarations
++ \row
++ \li Apple
++ \li \l{apple-usage-description}{Usage description}
++ \li \c NSCameraUsageDescription
++ \row
++ \li Android
++ \li \l{android-uses-permission}{\c{uses-permission}}
++ \li \c android.permission.CAMERA
++ \include permissions.qdocinc end-usage-declarations
++
++ \include permissions.qdocinc permission-metadata
++ */
++
++ QT_PERMISSION_IMPL_COMMON(QCameraPermission)
++ : u{} // stateless, atm
++ {}
++
++ /*!
++ \class QMicrophonePermission
++ \brief Access the microphone for monitoring or recording sound.
++
++ \section1 Requirements
++
++ \include permissions.qdocinc begin-usage-declarations
++ \row
++ \li Apple
++ \li \l{apple-usage-description}{Usage description}
++ \li \c NSMicrophoneUsageDescription
++ \row
++ \li Android
++ \li \l{android-uses-permission}{\c{uses-permission}}
++ \li \c android.permission.RECORD_AUDIO
++ \include permissions.qdocinc end-usage-declarations
++
++ \include permissions.qdocinc permission-metadata
++ */
++
++ QT_PERMISSION_IMPL_COMMON(QMicrophonePermission)
++ : u{} // stateless, atm
++ {}
++
++ /*!
++ \class QBluetoothPermission
++ \brief Access Bluetooth peripherals.
++
++ \section1 Requirements
++
++ \include permissions.qdocinc begin-usage-declarations
++ \row
++ \li Apple
++ \li \l{apple-usage-description}{Usage description}
++ \li \c NSBluetoothAlwaysUsageDescription
++ \row
++ \li Android
++ \li \l{android-uses-permission}{\c{uses-permission}}
++ \li Up to Android 11 (API Level < 31):
++ \list
++ \li \c android.permission.BLUETOOTH
++ \li \c android.permission.ACCESS_FINE_LOCATION
++ \endlist
++
++ Starting from Android 12 (API Level >= 31):
++ \list
++ \li \c android.permission.BLUETOOTH_ADVERTISE
++ \li \c android.permission.BLUETOOTH_CONNECT
++ \li \c android.permission.BLUETOOTH_SCAN
++ \endlist
++ \include permissions.qdocinc end-usage-declarations
++
++ \note Since Qt 6.8.1, the ACCESS_FINE_LOCATION permission is no longer
++ requested if API Level >= 31. This
++ \l {Android Bluetooth Permissions}{may limit some Bluetooth scan results}.
++ Users needing these results need
++ to request the location permission separately (see
++ \l {QLocationPermission::Precise}{precise location}) and ensure that
++ \c {BLUETOOTH_SCAN} permission doesn't have the
++ \c {android:usesPermissionFlags="neverForLocation"} attribute set.
++ For setting and customizing permissions in the application manifest,
++ \l {Qt Permissions and Features}{see this guide}.
++
++ \include permissions.qdocinc permission-metadata
++ */
++
++ QT_PERMISSION_IMPL_COMMON(QBluetoothPermission)
++ : u{ShortData{CommunicationMode::Default, {}}}
++ {}
++
++ /*!
++ \enum QBluetoothPermission::CommunicationMode
++ \since 6.6
++
++ This enum is used to control the allowed Bluetooth communication modes.
++
++ \value Access Allow this device to access other Bluetooth devices. This
++ includes scanning for nearby devices and connecting to them.
++ \value Advertise Allow other Bluetooth devices to discover this device.
++ \value Default This configuration is used by default.
++
++ \note The fine-grained permissions are currently supported only on
++ Android 12 and newer. On older Android versions, as well as on Apple
++ operating systems, any mode results in full Bluetooth access.
++ */
++
++ /*!
++ \since 6.6
++
++ Sets the allowed Bluetooth communication modes to \a modes.
++
++ \note A default-constructed instance of \l {QBluetoothPermission::}
++ {CommunicationModes} has no sense, so an attempt to set such a mode will
++ raise a \c {qWarning()} and fall back to using the
++ \l {QBluetoothPermission::}{Default} mode.
++ */
++ void QBluetoothPermission::setCommunicationModes(CommunicationModes modes)
++ {
++ if (modes == CommunicationModes{}) {
++ qCWarning(lcPermissions, "QBluetoothPermission: trying to set an invalid empty mode. "
++ "Falling back to CommunicationMode::Default.");
++ u.data.mode = Default;
++ } else {
++ u.data.mode = static_cast<CommunicationMode>(modes.toInt());
++ }
++ }
++
++ /*!
++ \since 6.6
++
++ Returns the allowed Bluetooth communication modes.
++ */
++ QBluetoothPermission::CommunicationModes QBluetoothPermission::communicationModes() const
++ {
++ return u.data.mode;
++ }
++
++ /*!
++ \class QLocationPermission
++ \brief Access the user's location.
++
++ By default the request is for approximate accuracy,
++ and only while the application is in use. Use
++ setAccuracy() and/or setAvailability() to override
++ the default.
++
++ \section1 Requirements
++
++ \include permissions.qdocinc begin-usage-declarations
++ \row
++ \li \macos
++ \li \l{apple-usage-description}{Usage description}
++ \li \c NSLocationUsageDescription
++ \row
++ \li iOS
++ \li \l{apple-usage-description}{Usage description}
++ \li \c NSLocationWhenInUseUsageDescription, and
++ \c NSLocationAlwaysAndWhenInUseUsageDescription if requesting
++ QLocationPermission::Always
++ \row
++ \li Android
++ \li \l{android-uses-permission}{\c{uses-permission}}
++ \li \list
++ \li \c android.permission.ACCESS_FINE_LOCATION for QLocationPermission::Precise
++ \li \c android.permission.ACCESS_COARSE_LOCATION for QLocationPermission::Approximate
++ \li \c android.permission.ACCESS_BACKGROUND_LOCATION for QLocationPermission::Always
++ \endlist
++ \note QLocationPermission::Always \c uses-permission string has
++ to be combined with one or both of QLocationPermission::Precise
++ and QLocationPermission::Approximate strings.
++ \include permissions.qdocinc end-usage-declarations
++
++ \include permissions.qdocinc permission-metadata
++ */
++
++ QT_PERMISSION_IMPL_COMMON(QLocationPermission)
++ : u{ShortData{Accuracy::Approximate, Availability::WhenInUse, {}}}
++ {}
++
++ /*!
++ \enum QLocationPermission::Accuracy
++
++ This enum is used to control the accuracy of the location data.
++
++ \value Approximate An approximate location is requested.
++ \value Precise A precise location is requested.
++ */
++
++ /*!
++ \enum QLocationPermission::Availability
++
++ This enum is used to control the availability of the location data.
++
++ \value WhenInUse The location is only available only when the
++ application is in use.
++ \value Always The location is available at all times, including when
++ the application is in the background.
++ */
++
++ /*!
++ Sets the desired \a accuracy of the request.
++ */
++ void QLocationPermission::setAccuracy(Accuracy accuracy)
++ {
++ u.data.accuracy = accuracy;
++ }
++
++ /*!
++ Returns the accuracy of the request.
++ */
++ QLocationPermission::Accuracy QLocationPermission::accuracy() const
++ {
++ return u.data.accuracy;
++ }
++
++ /*!
++ Sets the desired \a availability of the request.
++ */
++ void QLocationPermission::setAvailability(Availability availability)
++ {
++ u.data.availability = availability;
++ }
++
++ /*!
++ Returns the availability of the request.
++ */
++ QLocationPermission::Availability QLocationPermission::availability() const
++ {
++ return u.data.availability;
++ }
++
++ /*!
++ \class QContactsPermission
++ \brief Access the user's contacts.
++
++ By default the request is for read-only access.
++ Use setAccessMode() to override the default.
++
++ \section1 Requirements
++
++ \include permissions.qdocinc begin-usage-declarations
++ \row
++ \li Apple
++ \li \l{apple-usage-description}{Usage description}
++ \li \c NSContactsUsageDescription
++ \row
++ \li Android
++ \li \l{android-uses-permission}{\c{uses-permission}}
++ \li \c android.permission.READ_CONTACTS. \c android.permission.WRITE_CONTACTS if
++ QContactsPermission::accessMode() is set to AccessMode::ReadWrite.
++ \include permissions.qdocinc end-usage-declarations
++
++ \include permissions.qdocinc permission-metadata
++ */
++
++ /*!
++ \enum QContactsPermission::AccessMode
++
++ This enum is used to control access to the contacts data.
++
++ \value ReadOnly Read-only access to the contacts data (the default).
++ \value ReadWrite Read and write access to the contacts data.
++
++ \sa setAccessMode, accessMode
++ */
++
++ QT_PERMISSION_IMPL_COMMON(QContactsPermission)
++ : u{ShortData{AccessMode::ReadOnly, {}}}
++ {}
++
++ /*!
++ Sets whether the request is for read-write (\a mode == AccessMode::ReadWrite) or
++ read-only (\a mode == AccessMode::ReadOnly) access to the contacts.
++ */
++ void QContactsPermission::setAccessMode(AccessMode mode)
++ {
++ u.data.mode = mode;
++ }
++
++ /*!
++ Returns AccessMode::ReadWrite when the request is for read-write and
++ AccessMode::ReadOnly when it is for read-only access to the contacts.
++ */
++ QContactsPermission::AccessMode QContactsPermission::accessMode() const
++ {
++ return u.data.mode;
++ }
++
++ /*!
++ \class QCalendarPermission
++ \brief Access the user's calendar.
++
++ By default the request is for read-only access.
++ Use setAccessMode() to override the default.
++
++ \section1 Requirements
++
++ \include permissions.qdocinc begin-usage-declarations
++ \row
++ \li Apple
++ \li \l{apple-usage-description}{Usage description}
++ \li \c NSCalendarsUsageDescription
++ \row
++ \li Android
++ \li \l{android-uses-permission}{\c{uses-permission}}
++ \li \c android.permission.READ_CALENDAR if QCalendarPermission::accessMode() is set
++ to AccessMode::ReadOnly or AccessMode::ReadWrite.
++ \c android.permission.WRITE_CALENDAR if QCalendarPermission::accessMode() is set
++ to AccessMode::WriteOnly or AccessMode::ReadWrite.
++ \include permissions.qdocinc end-usage-declarations
++
++ \include permissions.qdocinc permission-metadata
++ */
++
++ /*!
++ \enum QCalendarPermission::AccessMode
++
++ This enum is used to control access to the calendar data.
++
++ \value ReadOnly Read-only access to the calendar data (the default).
++ \value ReadWrite Read and write access to the calendar data.
++ \value [since 6.12] WriteOnly Write-only access to the calendar data.
++
++ \sa setAccessMode, accessMode
++ */
++
++ QT_PERMISSION_IMPL_COMMON(QCalendarPermission)
++ : u{ShortData{AccessMode::ReadOnly, {}}}
++ {}
++
++ /*!
++ Sets the access mode to \a mode.
++ */
++ void QCalendarPermission::setAccessMode(AccessMode mode)
++ {
++ u.data.mode = mode;
++ }
++
++ /*!
++ Returns the requested access mode.
++ */
++ QCalendarPermission::AccessMode QCalendarPermission::accessMode() const
++ {
++ return u.data.mode;
++ }
++
++ /*!
++ * \internal
++ */
++
++ QPermissionPlugin::~QPermissionPlugin() = default;
++
++ #ifndef QT_NO_DEBUG_STREAM
++ QDebug operator<<(QDebug debug, const QPermission &permission)
++ {
++ const auto verbosity = debug.verbosity();
++ QDebugStateSaver saver(debug);
++ debug.nospace().setVerbosity(0);
++ if (verbosity >= QDebug::DefaultVerbosity)
++ debug << permission.type().name() << "(";
++ debug << permission.status();
++ if (verbosity >= QDebug::DefaultVerbosity)
++ debug << ")";
++ return debug;
++ }
++ #endif
++
++ #undef QT_PERMISSION_IMPL_COMMON
++
++ #if !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WASM) && !defined(Q_OS_HARMONY) && !defined(Q_OS_UNIX)
++ // Default backend for platforms without a permission implementation.
++ // Always returns Granted, to match behavior when not using permission APIs
++ namespace QPermissions::Private
++ {
++ Qt::PermissionStatus checkPermission(const QPermission &permission)
++ {
++ qCDebug(lcPermissions) << "No permission backend on this platform."
++ << "Optimistically returning Granted for" << permission;
++ return Qt::PermissionStatus::Granted;
++ }
++
++ void requestPermission(const QPermission &permission, const PermissionCallback &callback)
++ {
++ qCDebug(lcPermissions) << "No permission backend on this platform."
++ << "Optimistically returning Granted for" << permission;
++ callback(Qt::PermissionStatus::Granted);
++ }
++ }
++ #endif
++
++ QT_END_NAMESPACE
++
++ #include "moc_qpermissions.cpp"