Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpermissions.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qpermissions.h"
7
8#include <QtCore/qshareddata.h>
9#include <QtCore/qdebug.h>
10
11QT_BEGIN_NAMESPACE
12
13Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
14
15/*!
16 \page permissions.html
17 \title Application Permissions
18 \brief Managing application permissions
19
20 Many features of today's devices and operating systems can have
21 significant privacy, security, and performance implications if
22 misused. It's therefore increasingly common for platforms to
23 require explicit consent from the user before accessing these
24 features.
25
26 The Qt permission APIs allow the application to check or request
27 permission for such features in a cross platform manner.
28
29 \section1 Usage
30
31 A feature that commonly requires user consent is access to the
32 microphone of the device. An application for recording voice
33 memos would perhaps look something like this initially:
34
35 \code
36 void VoiceMemoWidget::onRecordingInitiated()
37 {
38 m_microphone->startRecording();
39 }
40 \endcode
41
42 To ensure this application works well on platforms that
43 require user consent for microphone access we would extend
44 it like this:
45
46 \code
47 void VoiceMemoWidget::onRecordingInitiated()
48 {
49 QMicrophonePermission microphonePermission;
50 switch (qApp->checkPermission(microphonePermission)) {
51 case Qt::PermissionStatus::Undetermined:
52 qApp->requestPermission(microphonePermission, this,
53 &VoiceMemoWidget::onRecordingInitiated);
54 return;
55 case Qt::PermissionStatus::Denied:
56 m_permissionInstructionsDialog->show();
57 return;
58 case Qt::PermissionStatus::Granted:
59 m_microphone->startRecording();
60 }
61 }
62 \endcode
63
64 We first check if we already know the status of the microphone permission.
65 If we don't we initiate a permission request to determine the current
66 status, which will potentially ask the user for consent. We connect the
67 result of the request to the slot we're already in, so that we get another
68 chance at evaluating the permission status.
69
70 Once the permission status is known, either because we had been granted or
71 denied permission at an earlier time, or after getting the result back from
72 the request we just initiated, we redirect the user to a dialog explaining
73 why we can not record voice memos at this time (if the permission was denied),
74 or proceed to using the microphone (if permission was granted).
75
76 \note On \macOS and iOS permissions can currently only be requested for
77 GUI applications.
78
79 \section2 Declaring Permissions
80
81 Some platforms require that the permissions you request are declared
82 up front at build time.
83
84 \section3 Apple platforms
85 \target apple-usage-description
86
87 Each permission you request must be accompanied by a so called
88 \e {usage description} string in the application's
89 \l{Information Property List Files}{\c Info.plist}
90 file, describing why the application needs to access the given
91 permission. For example:
92
93 \badcode
94 <key>NSMicrophoneUsageDescription</key>
95 <string>The microphone is used to record voice memos.</string>
96 \endcode
97
98 The relevant usage description keys are described in the documentation
99 for each permission type.
100
101 To ensure the relevant permission backend is included with your
102 application, please \l{Information Property List Files}
103 {point the build system to your custom \c Info.plist}.
104
105 \sa {Information Property List Files}
106
107 \section3 Android
108 \target android-uses-permission
109
110 Each permission you request must be accompanied by a \c uses-permission
111 entry in the application's \c AndroidManifest.xml file. For example:
112
113 \badcode
114 <manifest ...>
115 <uses-permission android:name="android.permission.RECORD_AUDIO"/>
116 </manifest>
117 \endcode
118
119 To ensure the relevant permission backend is included with your
120 application, please \l {QT_ANDROID_PACKAGE_SOURCE_DIR}
121 {point the build system to your custom \c AndroidManifest.xml}
122 or use \l {qt_add_android_permission()}.
123
124 The relevant permission names are described in the documentation
125 for each permission type.
126
127 \sa {Qt Creator: Editing Manifest Files}
128
129 \section1 Available Permissions
130
131 The following permissions types are available:
132
133 \annotatedlist permissions
134
135 \section1 Best Practices
136
137 To ensure the best possible user experience for the end user we recommend
138 adopting the following best practices for managing application permissions:
139
140 \list
141
142 \li Request the minimal set of permissions needed. For example, if you only
143 need access to the microphone, do \e not request camera permission just in case.
144 Use the properties of individual permission types to limit the permission scope
145 even further, for example \l{QContactsPermission::setAccessMode()}
146 to request read only access.
147
148 \li Request permissions in response to specific actions by the user. For example,
149 defer requesting microphone permission until the user presses the button to record
150 audio. Associating the permission request to a specific action gives the user a clearer
151 context of why the permission is needed. Do \e not request all needed permission on
152 startup.
153
154 \li Present extra context and explanation if needed. Sometimes the action by the user
155 is not enough context. Consider presenting an explanation-dialog after the user has
156 initiated the action, but before requesting the permission, so the user is aware of
157 what's about to happen when the system permission dialog subsequently pops up.
158
159 \li Be transparent and explicit about why permissions are needed. In explanation
160 dialogs and usage descriptions, be transparent about why the particular permission
161 is needed for your application to provide a specific feature, so users can make
162 informed decisions.
163
164 \li Account for denied permissions. The permissions you request may be denied
165 for various reasons. You should always account for this situation, by gracefully
166 degrading the experience of your application, and presenting clear explanations
167 the user about the situation.
168
169 \li Never request permissions from a library. The request of permissions should
170 be done as close as possible to the user, where the information needed to make
171 good decisions on the points above is available. Libraries can check permissions,
172 to ensure they have the prerequisites for doing their work, but if the permission
173 is undetermined or denied this should be reflected through the library's API,
174 so that the application in turn can request the necessary permissions.
175
176 \endlist
177*/
178
179
180/*!
181 \class QPermission
182 \inmodule QtCore
183 \inheaderfile QPermissions
184 \since 6.5
185 \brief An opaque wrapper of a typed permission.
186
187 The QPermission class is an opaque wrapper of a \l{typed permission},
188 used when checking or requesting permissions. You do not need to construct
189 this type explicitly, as the type is automatically used when checking or
190 requesting permissions:
191
192 \code
193 qApp->checkPermission(QCameraPermission{});
194 \endcode
195
196 When requesting permissions, the given functor will
197 be passed an instance of a QPermission, which can be used
198 to check the result of the request:
199
200 \code
201 qApp->requestPermission(QCameraPermission{}, [](const QPermission &permission) {
202 if (permission.status() == Qt::PermissionStatus::Granted)
203 takePhoto();
204 });
205 \endcode
206
207 To inspect the properties of the original, typed permission,
208 use the \l {QPermission::}{value()} function:
209
210 \code
211 QLocationPermission locationPermission;
212 locationPermission.setAccuracy(QLocationPermission::Precise);
213 qApp->requestPermission(locationPermission, this, &LocationWidget::permissionUpdated);
214 \endcode
215
216 \code
217 void LocationWidget::permissionUpdated(const QPermission &permission)
218 {
219 if (permission.status() != Qt::PermissionStatus::Granted)
220 return;
221 auto locationPermission = permission.value<QLocationPermission>();
222 if (!locationPermission || locationPermission->accuracy() != QLocationPermission::Precise)
223 return;
224 updatePreciseLocation();
225 }
226 \endcode
227
228 \target typed permission
229 \section2 Typed Permissions
230
231 The following permissions are available:
232
233 \annotatedlist permissions
234
235 \sa {Application Permissions}
236*/
237
238/*!
239 \fn template <typename T, QPermission::if_permission<T>> QPermission::QPermission(const T &type)
240
241 Constructs a permission from the given \l{typed permission} \a type.
242
243 You do not need to construct this type explicitly, as the type is automatically
244 used when checking or requesting permissions.
245
246 \constraints
247 \c T is one of the \l{typed permission} classes:
248
249 \annotatedlist permissions
250*/
251
252/*!
253 \fn template <typename T, QPermission::if_permission<T>> std::optional<T> QPermission::value() const
254
255 Returns the \l{typed permission} of type \c T, or \c{std::nullopt} if this
256 QPermission object doesn't contain one.
257
258 Use type() for dynamically choosing which typed permission to request.
259
260 \constraints
261 \c T is one of the \l{typed permission} classes:
262
263 \annotatedlist permissions
264*/
265
266/*!
267 \fn Qt::PermissionStatus QPermission::status() const
268 Returns the status of the permission.
269*/
270
271/*!
272 \fn QMetaType QPermission::type() const
273 Returns the type of the permission.
274*/
275
276/*
277 \internal
278*/
279const void *QPermission::data(QMetaType requestedType) const
280{
281 const auto actualType = type();
282 if (requestedType != actualType)
283 return nullptr;
284 return m_data.data();
285}
286
287// check alignof(AlignmentCheck) instead of alignof(void*), in case
288// pointers have different alignment inside structs:
289struct AlignmentCheck { void *p; };
290
291#define QT_PERMISSION_IMPL_COMMON(ClassName)
292 /* Class##Private is unused until we need it: */
293 static_assert(sizeof(ClassName) == sizeof(void*),
294 "You have added too many members to " #ClassName "::ShortData. "
295 "Decrease their size or switch to using a d-pointer.");
296 static_assert(alignof(ClassName) == alignof(AlignmentCheck),
297 "You have added members to " #ClassName "::ShortData that are overaligned. "
298 "Decrease their alignment or switch to using a d-pointer.");
299 ClassName::ClassName(const ClassName &other) noexcept = default;
300 ClassName::~ClassName() = default;
301 ClassName &ClassName::operator=(const ClassName &other) noexcept = default;
302 ClassName::ClassName()
303 /* impl supplied by caller */
304
305
306/*!
307 \class QCameraPermission
308 \brief Access the camera for taking pictures or videos.
309
310 \section1 Requirements
311
312 \include permissions.qdocinc begin-usage-declarations
313 \row
314 \li Apple
315 \li \l{apple-usage-description}{Usage description}
316 \li \c NSCameraUsageDescription
317 \row
318 \li Android
319 \li \l{android-uses-permission}{\c{uses-permission}}
320 \li \c android.permission.CAMERA
321 \include permissions.qdocinc end-usage-declarations
322
323 \include permissions.qdocinc permission-metadata
324*/
325
327 : u{} // stateless, atm
328{}
329
330/*!
331 \class QMicrophonePermission
332 \brief Access the microphone for monitoring or recording sound.
333
334 \section1 Requirements
335
336 \include permissions.qdocinc begin-usage-declarations
337 \row
338 \li Apple
339 \li \l{apple-usage-description}{Usage description}
340 \li \c NSMicrophoneUsageDescription
341 \row
342 \li Android
343 \li \l{android-uses-permission}{\c{uses-permission}}
344 \li \c android.permission.RECORD_AUDIO
345 \include permissions.qdocinc end-usage-declarations
346
347 \include permissions.qdocinc permission-metadata
348*/
349
350QT_PERMISSION_IMPL_COMMON(QMicrophonePermission)
351 : u{} // stateless, atm
352{}
353
354/*!
355 \class QBluetoothPermission
356 \brief Access Bluetooth peripherals.
357
358 \section1 Requirements
359
360 \include permissions.qdocinc begin-usage-declarations
361 \row
362 \li Apple
363 \li \l{apple-usage-description}{Usage description}
364 \li \c NSBluetoothAlwaysUsageDescription
365 \row
366 \li Android
367 \li \l{android-uses-permission}{\c{uses-permission}}
368 \li Up to Android 11 (API Level < 31):
369 \list
370 \li \c android.permission.BLUETOOTH
371 \li \c android.permission.ACCESS_FINE_LOCATION
372 \endlist
373
374 Starting from Android 12 (API Level >= 31):
375 \list
376 \li \c android.permission.BLUETOOTH_ADVERTISE
377 \li \c android.permission.BLUETOOTH_CONNECT
378 \li \c android.permission.BLUETOOTH_SCAN
379 \endlist
380 \include permissions.qdocinc end-usage-declarations
381
382 \note Since Qt 6.8.1, the ACCESS_FINE_LOCATION permission is no longer
383 requested if API Level >= 31. This
384 \l {Android Bluetooth Permissions}{may limit some Bluetooth scan results}.
385 Users needing these results need
386 to request the location permission separately (see
387 \l {QLocationPermission::Precise}{precise location}) and ensure that
388 \c {BLUETOOTH_SCAN} permission doesn't have the
389 \c {android:usesPermissionFlags="neverForLocation"} attribute set.
390 For setting and customizing permissions in the application manifest,
391 \l {Qt Permissions and Features}{see this guide}.
392
393 \include permissions.qdocinc permission-metadata
394*/
395
396QT_PERMISSION_IMPL_COMMON(QBluetoothPermission)
397 : u{ShortData{CommunicationMode::Default, {}}}
398{}
399
400/*!
401 \enum QBluetoothPermission::CommunicationMode
402 \since 6.6
403
404 This enum is used to control the allowed Bluetooth communication modes.
405
406 \value Access Allow this device to access other Bluetooth devices. This
407 includes scanning for nearby devices and connecting to them.
408 \value Advertise Allow other Bluetooth devices to discover this device.
409 \value Default This configuration is used by default.
410
411 \note The fine-grained permissions are currently supported only on
412 Android 12 and newer. On older Android versions, as well as on Apple
413 operating systems, any mode results in full Bluetooth access.
414*/
415
416/*!
417 \since 6.6
418
419 Sets the allowed Bluetooth communication modes to \a modes.
420
421 \note A default-constructed instance of \l {QBluetoothPermission::}
422 {CommunicationModes} has no sense, so an attempt to set such a mode will
423 raise a \c {qWarning()} and fall back to using the
424 \l {QBluetoothPermission::}{Default} mode.
425*/
426void QBluetoothPermission::setCommunicationModes(CommunicationModes modes)
427{
428 if (modes == CommunicationModes{}) {
429 qCWarning(lcPermissions, "QBluetoothPermission: trying to set an invalid empty mode. "
430 "Falling back to CommunicationMode::Default.");
431 u.data.mode = Default;
432 } else {
433 u.data.mode = static_cast<CommunicationMode>(modes.toInt());
434 }
435}
436
437/*!
438 \since 6.6
439
440 Returns the allowed Bluetooth communication modes.
441*/
442QBluetoothPermission::CommunicationModes QBluetoothPermission::communicationModes() const
443{
444 return u.data.mode;
445}
446
447/*!
448 \class QLocationPermission
449 \brief Access the user's location.
450
451 By default the request is for approximate accuracy,
452 and only while the application is in use. Use
453 setAccuracy() and/or setAvailability() to override
454 the default.
455
456 \section1 Requirements
457
458 \include permissions.qdocinc begin-usage-declarations
459 \row
460 \li \macos
461 \li \l{apple-usage-description}{Usage description}
462 \li \c NSLocationUsageDescription
463 \row
464 \li iOS
465 \li \l{apple-usage-description}{Usage description}
466 \li \c NSLocationWhenInUseUsageDescription, and
467 \c NSLocationAlwaysAndWhenInUseUsageDescription if requesting
468 QLocationPermission::Always
469 \row
470 \li Android
471 \li \l{android-uses-permission}{\c{uses-permission}}
472 \li \list
473 \li \c android.permission.ACCESS_FINE_LOCATION for QLocationPermission::Precise
474 \li \c android.permission.ACCESS_COARSE_LOCATION for QLocationPermission::Approximate
475 \li \c android.permission.ACCESS_BACKGROUND_LOCATION for QLocationPermission::Always
476 \endlist
477 \note QLocationPermission::Always \c uses-permission string has
478 to be combined with one or both of QLocationPermission::Precise
479 and QLocationPermission::Approximate strings.
480 \include permissions.qdocinc end-usage-declarations
481
482 \include permissions.qdocinc permission-metadata
483*/
484
485QT_PERMISSION_IMPL_COMMON(QLocationPermission)
486 : u{ShortData{Accuracy::Approximate, Availability::WhenInUse, {}}}
487{}
488
489/*!
490 \enum QLocationPermission::Accuracy
491
492 This enum is used to control the accuracy of the location data.
493
494 \value Approximate An approximate location is requested.
495 \value Precise A precise location is requested.
496*/
497
498/*!
499 \enum QLocationPermission::Availability
500
501 This enum is used to control the availability of the location data.
502
503 \value WhenInUse The location is only available only when the
504 application is in use.
505 \value Always The location is available at all times, including when
506 the application is in the background.
507*/
508
509/*!
510 Sets the desired \a accuracy of the request.
511*/
512void QLocationPermission::setAccuracy(Accuracy accuracy)
513{
514 u.data.accuracy = accuracy;
515}
516
517/*!
518 Returns the accuracy of the request.
519*/
520QLocationPermission::Accuracy QLocationPermission::accuracy() const
521{
522 return u.data.accuracy;
523}
524
525/*!
526 Sets the desired \a availability of the request.
527*/
528void QLocationPermission::setAvailability(Availability availability)
529{
530 u.data.availability = availability;
531}
532
533/*!
534 Returns the availability of the request.
535*/
537{
538 return u.data.availability;
539}
540
541/*!
542 \class QContactsPermission
543 \brief Access the user's contacts.
544
545 By default the request is for read-only access.
546 Use setAccessMode() to override the default.
547
548 \section1 Requirements
549
550 \include permissions.qdocinc begin-usage-declarations
551 \row
552 \li Apple
553 \li \l{apple-usage-description}{Usage description}
554 \li \c NSContactsUsageDescription
555 \row
556 \li Android
557 \li \l{android-uses-permission}{\c{uses-permission}}
558 \li \c android.permission.READ_CONTACTS. \c android.permission.WRITE_CONTACTS if
559 QContactsPermission::accessMode() is set to AccessMode::ReadWrite.
560 \include permissions.qdocinc end-usage-declarations
561
562 \include permissions.qdocinc permission-metadata
563*/
564
565/*!
566 \enum QContactsPermission::AccessMode
567
568 This enum is used to control access to the contacts data.
569
570 \value ReadOnly Read-only access to the contacts data (the default).
571 \value ReadWrite Read and write access to the contacts data.
572
573 \sa setAccessMode, accessMode
574*/
575
576QT_PERMISSION_IMPL_COMMON(QContactsPermission)
577 : u{ShortData{AccessMode::ReadOnly, {}}}
578{}
579
580/*!
581 Sets whether the request is for read-write (\a mode == AccessMode::ReadWrite) or
582 read-only (\a mode == AccessMode::ReadOnly) access to the contacts.
583*/
584void QContactsPermission::setAccessMode(AccessMode mode)
585{
586 u.data.mode = mode;
587}
588
589/*!
590 Returns AccessMode::ReadWrite when the request is for read-write and
591 AccessMode::ReadOnly when it is for read-only access to the contacts.
592*/
593QContactsPermission::AccessMode QContactsPermission::accessMode() const
594{
595 return u.data.mode;
596}
597
598/*!
599 \class QCalendarPermission
600 \brief Access the user's calendar.
601
602 By default the request is for read-only access.
603 Use setAccessMode() to override the default.
604
605 \section1 Requirements
606
607 \include permissions.qdocinc begin-usage-declarations
608 \row
609 \li Apple
610 \li \l{apple-usage-description}{Usage description}
611 \li \c NSCalendarsUsageDescription
612 \row
613 \li Android
614 \li \l{android-uses-permission}{\c{uses-permission}}
615 \li \c android.permission.READ_CALENDAR. \c android.permission.WRITE_CALENDAR if
616 QCalendarPermission::accessMode() is set to AccessMode::ReadWrite.
617 \include permissions.qdocinc end-usage-declarations
618
619 \include permissions.qdocinc permission-metadata
620*/
621
622/*!
623 \enum QCalendarPermission::AccessMode
624
625 This enum is used to control access to the calendar data.
626
627 \value ReadOnly Read-only access to the calendar data (the default).
628 \value ReadWrite Read and write access to the calendar data.
629
630 \sa setAccessMode, accessMode
631*/
632
633QT_PERMISSION_IMPL_COMMON(QCalendarPermission)
634 : u{ShortData{AccessMode::ReadOnly, {}}}
635{}
636
637/*!
638 Sets whether the request is for read-write (\a mode == AccessMode::ReadWrite) or
639 read-only (\a mode == AccessMode::ReadOnly) access to the calendar.
640*/
641void QCalendarPermission::setAccessMode(AccessMode mode)
642{
643 u.data.mode = mode;
644}
645
646/*!
647 Returns AccessMode::ReadWrite when the request is for read-write and
648 AccessMode::ReadOnly when it is for read-only access to the calendar.
649*/
650QCalendarPermission::AccessMode QCalendarPermission::accessMode() const
651{
652 return u.data.mode;
653}
654
655/*!
656 * \internal
657*/
658
659QPermissionPlugin::~QPermissionPlugin() = default;
660
661#ifndef QT_NO_DEBUG_STREAM
662QDebug operator<<(QDebug debug, const QPermission &permission)
663{
664 const auto verbosity = debug.verbosity();
665 QDebugStateSaver saver(debug);
666 debug.nospace().setVerbosity(0);
667 if (verbosity >= QDebug::DefaultVerbosity)
668 debug << permission.type().name() << "(";
669 debug << permission.status();
670 if (verbosity >= QDebug::DefaultVerbosity)
671 debug << ")";
672 return debug;
673}
674#endif
675
676#undef QT_PERMISSION_IMPL_COMMON
677
678#if !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WASM)
679// Default backend for platforms without a permission implementation.
680// Always returns Granted, to match behavior when not using permission APIs
681// https://bugreports.qt.io/browse/QTBUG-90498?focusedCommentId=725085#comment-725085
682namespace QPermissions::Private
683{
685 {
686 qCDebug(lcPermissions) << "No permission backend on this platform."
687 << "Optimistically returning Granted for" << permission;
688 return Qt::PermissionStatus::Granted;
689 }
690
691 void requestPermission(const QPermission &permission, const PermissionCallback &callback)
692 {
693 qCDebug(lcPermissions) << "No permission backend on this platform."
694 << "Optimistically returning Granted for" << permission;
695 callback(Qt::PermissionStatus::Granted);
696 }
697}
698#endif
699
700QT_END_NAMESPACE
701
702#include "moc_qpermissions.cpp"
Access Bluetooth peripherals.
Access the user's calendar.
Access the user's contacts.
Access the user's location.
Availability
This enum is used to control the availability of the location data.
\inmodule QtCore \inheaderfile QPermissions
void requestPermission(const QPermission &permission, const PermissionCallback &callback)
Qt::PermissionStatus checkPermission(const QPermission &permission)
#define QT_PERMISSION_IMPL_COMMON(ClassName)