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