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