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 \note When using this API, the \c{<!-- %%INSERT_PERMISSIONS -->} tag must be present in
128 the AndroidManifest.xml. For further information on the use of this tag,
129 see \l {Qt Permissions and Features}
130
131 \sa {Qt Creator: Editing Manifest Files}.
132
133 \section1 Available Permissions
134
135 The following permissions types are available:
136
137 \annotatedlist permissions
138
139 \note The available permission types cover core functionality of Qt modules
140 like Qt Multimedia and Qt Positioning, but do not encompass all platform-specific
141 permissions. Custom permission types are not currently supported.
142
143 \section1 Best Practices
144
145 To ensure the best possible user experience for the end user we recommend
146 adopting the following best practices for managing application permissions:
147
148 \list
149
150 \li Request the minimal set of permissions needed. For example, if you only
151 need access to the microphone, do \e not request camera permission just in case.
152 Use the properties of individual permission types to limit the permission scope
153 even further, for example \l{QContactsPermission::setAccessMode()}
154 to request read only access.
155
156 \li Request permissions in response to specific actions by the user. For example,
157 defer requesting microphone permission until the user presses the button to record
158 audio. Associating the permission request to a specific action gives the user a clearer
159 context of why the permission is needed. Do \e not request all needed permission on
160 startup.
161
162 \li Present extra context and explanation if needed. Sometimes the action by the user
163 is not enough context. Consider presenting an explanation-dialog after the user has
164 initiated the action, but before requesting the permission, so the user is aware of
165 what's about to happen when the system permission dialog subsequently pops up.
166
167 \li Be transparent and explicit about why permissions are needed. In explanation
168 dialogs and usage descriptions, be transparent about why the particular permission
169 is needed for your application to provide a specific feature, so users can make
170 informed decisions.
171
172 \li Account for denied permissions. The permissions you request may be denied
173 for various reasons. You should always account for this situation, by gracefully
174 degrading the experience of your application, and presenting clear explanations
175 the user about the situation.
176
177 \li Never request permissions from a library. The request of permissions should
178 be done as close as possible to the user, where the information needed to make
179 good decisions on the points above is available. Libraries can check permissions,
180 to ensure they have the prerequisites for doing their work, but if the permission
181 is undetermined or denied this should be reflected through the library's API,
182 so that the application in turn can request the necessary permissions.
183
184 \endlist
185*/
186
187
188/*!
189 \class QPermission
190 \inmodule QtCore
191 \inheaderfile QPermissions
192 \since 6.5
193 \brief An opaque wrapper of a typed permission.
194
195 The QPermission class is an opaque wrapper of a \l{typed permission},
196 used when checking or requesting permissions. You do not need to construct
197 this type explicitly, as the type is automatically used when checking or
198 requesting permissions:
199
200 \code
201 qApp->checkPermission(QCameraPermission{});
202 \endcode
203
204 When requesting permissions, the given functor will
205 be passed an instance of a QPermission, which can be used
206 to check the result of the request:
207
208 \code
209 qApp->requestPermission(QCameraPermission{}, [](const QPermission &permission) {
210 if (permission.status() == Qt::PermissionStatus::Granted)
211 takePhoto();
212 });
213 \endcode
214
215 To inspect the properties of the original, typed permission,
216 use the \l {QPermission::}{value()} function:
217
218 \code
219 QLocationPermission locationPermission;
220 locationPermission.setAccuracy(QLocationPermission::Precise);
221 qApp->requestPermission(locationPermission, this, &LocationWidget::permissionUpdated);
222 \endcode
223
224 \code
225 void LocationWidget::permissionUpdated(const QPermission &permission)
226 {
227 if (permission.status() != Qt::PermissionStatus::Granted)
228 return;
229 auto locationPermission = permission.value<QLocationPermission>();
230 if (!locationPermission || locationPermission->accuracy() != QLocationPermission::Precise)
231 return;
232 updatePreciseLocation();
233 }
234 \endcode
235
236 \target typed permission
237 \section2 Typed Permissions
238
239 The following permissions are available:
240
241 \annotatedlist permissions
242
243 \sa {Application Permissions}
244*/
245
246/*!
247 \fn template <typename T, QPermission::if_permission<T>> QPermission::QPermission(const T &type)
248
249 Constructs a permission from the given \l{typed permission} \a type.
250
251 You do not need to construct this type explicitly, as the type is automatically
252 used when checking or requesting permissions.
253
254 \constraints
255 \c T is one of the \l{typed permission} classes:
256
257 \annotatedlist permissions
258*/
259
260/*!
261 \fn template <typename T, QPermission::if_permission<T>> std::optional<T> QPermission::value() const
262
263 Returns the \l{typed permission} of type \c T, or \c{std::nullopt} if this
264 QPermission object doesn't contain one.
265
266 Use type() for dynamically choosing which typed permission to request.
267
268 \constraints
269 \c T is one of the \l{typed permission} classes:
270
271 \annotatedlist permissions
272*/
273
274/*!
275 \fn Qt::PermissionStatus QPermission::status() const
276 Returns the status of the permission.
277*/
278
279/*!
280 \fn QMetaType QPermission::type() const
281 Returns the type of the permission.
282*/
283
284/*
285 \internal
286*/
287const void *QPermission::data(QMetaType requestedType) const
288{
289 const auto actualType = type();
290 if (requestedType != actualType)
291 return nullptr;
292 return m_data.data();
293}
294
295// check alignof(AlignmentCheck) instead of alignof(void*), in case
296// pointers have different alignment inside structs:
297struct AlignmentCheck { void *p; };
298
299#define QT_PERMISSION_IMPL_COMMON(ClassName)
300 /* Class##Private is unused until we need it: */
301 static_assert(sizeof(ClassName) == sizeof(void*),
302 "You have added too many members to " #ClassName "::ShortData. "
303 "Decrease their size or switch to using a d-pointer.");
304 static_assert(alignof(ClassName) == alignof(AlignmentCheck),
305 "You have added members to " #ClassName "::ShortData that are overaligned. "
306 "Decrease their alignment or switch to using a d-pointer.");
307 ClassName::ClassName(const ClassName &other) noexcept = default;
308 ClassName::~ClassName() = default;
309 ClassName &ClassName::operator=(const ClassName &other) noexcept = default;
310 ClassName::ClassName()
311 /* impl supplied by caller */
312
313
314/*!
315 \class QCameraPermission
316 \brief Access the camera for taking pictures or videos.
317
318 \section1 Requirements
319
320 \include permissions.qdocinc begin-usage-declarations
321 \row
322 \li Apple
323 \li \l{apple-usage-description}{Usage description}
324 \li \c NSCameraUsageDescription
325 \row
326 \li Android
327 \li \l{android-uses-permission}{\c{uses-permission}}
328 \li \c android.permission.CAMERA
329 \include permissions.qdocinc end-usage-declarations
330
331 \include permissions.qdocinc permission-metadata
332*/
333
335 : u{} // stateless, atm
336{}
337
338/*!
339 \class QMicrophonePermission
340 \brief Access the microphone for monitoring or recording sound.
341
342 \section1 Requirements
343
344 \include permissions.qdocinc begin-usage-declarations
345 \row
346 \li Apple
347 \li \l{apple-usage-description}{Usage description}
348 \li \c NSMicrophoneUsageDescription
349 \row
350 \li Android
351 \li \l{android-uses-permission}{\c{uses-permission}}
352 \li \c android.permission.RECORD_AUDIO
353 \include permissions.qdocinc end-usage-declarations
354
355 \include permissions.qdocinc permission-metadata
356*/
357
358QT_PERMISSION_IMPL_COMMON(QMicrophonePermission)
359 : u{} // stateless, atm
360{}
361
362/*!
363 \class QBluetoothPermission
364 \brief Access Bluetooth peripherals.
365
366 \section1 Requirements
367
368 \include permissions.qdocinc begin-usage-declarations
369 \row
370 \li Apple
371 \li \l{apple-usage-description}{Usage description}
372 \li \c NSBluetoothAlwaysUsageDescription
373 \row
374 \li Android
375 \li \l{android-uses-permission}{\c{uses-permission}}
376 \li Up to Android 11 (API Level < 31):
377 \list
378 \li \c android.permission.BLUETOOTH
379 \li \c android.permission.ACCESS_FINE_LOCATION
380 \endlist
381
382 Starting from Android 12 (API Level >= 31):
383 \list
384 \li \c android.permission.BLUETOOTH_ADVERTISE
385 \li \c android.permission.BLUETOOTH_CONNECT
386 \li \c android.permission.BLUETOOTH_SCAN
387 \endlist
388 \include permissions.qdocinc end-usage-declarations
389
390 \note Since Qt 6.8.1, the ACCESS_FINE_LOCATION permission is no longer
391 requested if API Level >= 31. This
392 \l {Android Bluetooth Permissions}{may limit some Bluetooth scan results}.
393 Users needing these results need
394 to request the location permission separately (see
395 \l {QLocationPermission::Precise}{precise location}) and ensure that
396 \c {BLUETOOTH_SCAN} permission doesn't have the
397 \c {android:usesPermissionFlags="neverForLocation"} attribute set.
398 For setting and customizing permissions in the application manifest,
399 \l {Qt Permissions and Features}{see this guide}.
400
401 \include permissions.qdocinc permission-metadata
402*/
403
404QT_PERMISSION_IMPL_COMMON(QBluetoothPermission)
405 : u{ShortData{CommunicationMode::Default, {}}}
406{}
407
408/*!
409 \enum QBluetoothPermission::CommunicationMode
410 \since 6.6
411
412 This enum is used to control the allowed Bluetooth communication modes.
413
414 \value Access Allow this device to access other Bluetooth devices. This
415 includes scanning for nearby devices and connecting to them.
416 \value Advertise Allow other Bluetooth devices to discover this device.
417 \value Default This configuration is used by default.
418
419 \note The fine-grained permissions are currently supported only on
420 Android 12 and newer. On older Android versions, as well as on Apple
421 operating systems, any mode results in full Bluetooth access.
422*/
423
424/*!
425 \since 6.6
426
427 Sets the allowed Bluetooth communication modes to \a modes.
428
429 \note A default-constructed instance of \l {QBluetoothPermission::}
430 {CommunicationModes} has no sense, so an attempt to set such a mode will
431 raise a \c {qWarning()} and fall back to using the
432 \l {QBluetoothPermission::}{Default} mode.
433*/
434void QBluetoothPermission::setCommunicationModes(CommunicationModes modes)
435{
436 if (modes == CommunicationModes{}) {
437 qCWarning(lcPermissions, "QBluetoothPermission: trying to set an invalid empty mode. "
438 "Falling back to CommunicationMode::Default.");
439 u.data.mode = Default;
440 } else {
441 u.data.mode = static_cast<CommunicationMode>(modes.toInt());
442 }
443}
444
445/*!
446 \since 6.6
447
448 Returns the allowed Bluetooth communication modes.
449*/
450QBluetoothPermission::CommunicationModes QBluetoothPermission::communicationModes() const
451{
452 return u.data.mode;
453}
454
455/*!
456 \class QLocationPermission
457 \brief Access the user's location.
458
459 By default the request is for approximate accuracy,
460 and only while the application is in use. Use
461 setAccuracy() and/or setAvailability() to override
462 the default.
463
464 \section1 Requirements
465
466 \include permissions.qdocinc begin-usage-declarations
467 \row
468 \li \macos
469 \li \l{apple-usage-description}{Usage description}
470 \li \c NSLocationUsageDescription
471 \row
472 \li iOS
473 \li \l{apple-usage-description}{Usage description}
474 \li \c NSLocationWhenInUseUsageDescription, and
475 \c NSLocationAlwaysAndWhenInUseUsageDescription if requesting
476 QLocationPermission::Always
477 \row
478 \li Android
479 \li \l{android-uses-permission}{\c{uses-permission}}
480 \li \list
481 \li \c android.permission.ACCESS_FINE_LOCATION for QLocationPermission::Precise
482 \li \c android.permission.ACCESS_COARSE_LOCATION for QLocationPermission::Approximate
483 \li \c android.permission.ACCESS_BACKGROUND_LOCATION for QLocationPermission::Always
484 \endlist
485 \note QLocationPermission::Always \c uses-permission string has
486 to be combined with one or both of QLocationPermission::Precise
487 and QLocationPermission::Approximate strings.
488 \include permissions.qdocinc end-usage-declarations
489
490 \include permissions.qdocinc permission-metadata
491*/
492
493QT_PERMISSION_IMPL_COMMON(QLocationPermission)
494 : u{ShortData{Accuracy::Approximate, Availability::WhenInUse, {}}}
495{}
496
497/*!
498 \enum QLocationPermission::Accuracy
499
500 This enum is used to control the accuracy of the location data.
501
502 \value Approximate An approximate location is requested.
503 \value Precise A precise location is requested.
504*/
505
506/*!
507 \enum QLocationPermission::Availability
508
509 This enum is used to control the availability of the location data.
510
511 \value WhenInUse The location is only available only when the
512 application is in use.
513 \value Always The location is available at all times, including when
514 the application is in the background.
515*/
516
517/*!
518 Sets the desired \a accuracy of the request.
519*/
520void QLocationPermission::setAccuracy(Accuracy accuracy)
521{
522 u.data.accuracy = accuracy;
523}
524
525/*!
526 Returns the accuracy of the request.
527*/
528QLocationPermission::Accuracy QLocationPermission::accuracy() const
529{
530 return u.data.accuracy;
531}
532
533/*!
534 Sets the desired \a availability of the request.
535*/
536void QLocationPermission::setAvailability(Availability availability)
537{
538 u.data.availability = availability;
539}
540
541/*!
542 Returns the availability of the request.
543*/
545{
546 return u.data.availability;
547}
548
549/*!
550 \class QContactsPermission
551 \brief Access the user's contacts.
552
553 By default the request is for read-only access.
554 Use setAccessMode() to override the default.
555
556 \section1 Requirements
557
558 \include permissions.qdocinc begin-usage-declarations
559 \row
560 \li Apple
561 \li \l{apple-usage-description}{Usage description}
562 \li \c NSContactsUsageDescription
563 \row
564 \li Android
565 \li \l{android-uses-permission}{\c{uses-permission}}
566 \li \c android.permission.READ_CONTACTS. \c android.permission.WRITE_CONTACTS if
567 QContactsPermission::accessMode() is set to AccessMode::ReadWrite.
568 \include permissions.qdocinc end-usage-declarations
569
570 \include permissions.qdocinc permission-metadata
571*/
572
573/*!
574 \enum QContactsPermission::AccessMode
575
576 This enum is used to control access to the contacts data.
577
578 \value ReadOnly Read-only access to the contacts data (the default).
579 \value ReadWrite Read and write access to the contacts data.
580
581 \sa setAccessMode, accessMode
582*/
583
584QT_PERMISSION_IMPL_COMMON(QContactsPermission)
585 : u{ShortData{AccessMode::ReadOnly, {}}}
586{}
587
588/*!
589 Sets whether the request is for read-write (\a mode == AccessMode::ReadWrite) or
590 read-only (\a mode == AccessMode::ReadOnly) access to the contacts.
591*/
592void QContactsPermission::setAccessMode(AccessMode mode)
593{
594 u.data.mode = mode;
595}
596
597/*!
598 Returns AccessMode::ReadWrite when the request is for read-write and
599 AccessMode::ReadOnly when it is for read-only access to the contacts.
600*/
601QContactsPermission::AccessMode QContactsPermission::accessMode() const
602{
603 return u.data.mode;
604}
605
606/*!
607 \class QCalendarPermission
608 \brief Access the user's calendar.
609
610 By default the request is for read-only access.
611 Use setAccessMode() to override the default.
612
613 \section1 Requirements
614
615 \include permissions.qdocinc begin-usage-declarations
616 \row
617 \li Apple
618 \li \l{apple-usage-description}{Usage description}
619 \li \c NSCalendarsUsageDescription
620 \row
621 \li Android
622 \li \l{android-uses-permission}{\c{uses-permission}}
623 \li \c android.permission.READ_CALENDAR. \c android.permission.WRITE_CALENDAR if
624 QCalendarPermission::accessMode() is set to AccessMode::ReadWrite.
625 \include permissions.qdocinc end-usage-declarations
626
627 \include permissions.qdocinc permission-metadata
628*/
629
630/*!
631 \enum QCalendarPermission::AccessMode
632
633 This enum is used to control access to the calendar data.
634
635 \value ReadOnly Read-only access to the calendar data (the default).
636 \value ReadWrite Read and write access to the calendar data.
637
638 \sa setAccessMode, accessMode
639*/
640
641QT_PERMISSION_IMPL_COMMON(QCalendarPermission)
642 : u{ShortData{AccessMode::ReadOnly, {}}}
643{}
644
645/*!
646 Sets whether the request is for read-write (\a mode == AccessMode::ReadWrite) or
647 read-only (\a mode == AccessMode::ReadOnly) access to the calendar.
648*/
649void QCalendarPermission::setAccessMode(AccessMode mode)
650{
651 u.data.mode = mode;
652}
653
654/*!
655 Returns AccessMode::ReadWrite when the request is for read-write and
656 AccessMode::ReadOnly when it is for read-only access to the calendar.
657*/
658QCalendarPermission::AccessMode QCalendarPermission::accessMode() const
659{
660 return u.data.mode;
661}
662
663/*!
664 * \internal
665*/
666
667QPermissionPlugin::~QPermissionPlugin() = default;
668
669#ifndef QT_NO_DEBUG_STREAM
670QDebug operator<<(QDebug debug, const QPermission &permission)
671{
672 const auto verbosity = debug.verbosity();
673 QDebugStateSaver saver(debug);
674 debug.nospace().setVerbosity(0);
675 if (verbosity >= QDebug::DefaultVerbosity)
676 debug << permission.type().name() << "(";
677 debug << permission.status();
678 if (verbosity >= QDebug::DefaultVerbosity)
679 debug << ")";
680 return debug;
681}
682#endif
683
684#undef QT_PERMISSION_IMPL_COMMON
685
686#if !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WASM)
687// Default backend for platforms without a permission implementation.
688// Always returns Granted, to match behavior when not using permission APIs
689// https://bugreports.qt.io/browse/QTBUG-90498?focusedCommentId=725085#comment-725085
690namespace QPermissions::Private
691{
693 {
694 qCDebug(lcPermissions) << "No permission backend on this platform."
695 << "Optimistically returning Granted for" << permission;
696 return Qt::PermissionStatus::Granted;
697 }
698
699 void requestPermission(const QPermission &permission, const PermissionCallback &callback)
700 {
701 qCDebug(lcPermissions) << "No permission backend on this platform."
702 << "Optimistically returning Granted for" << permission;
703 callback(Qt::PermissionStatus::Granted);
704 }
705}
706#endif
707
708QT_END_NAMESPACE
709
710#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)