QBluetoothPermission: introduce fine-grained permissions

This commit introduces fine-grained Bluetooth permissions control to
the QBluetoothPermission class.
For now the fine-tuning of the permissions is only supported on
Android. On Apple enabling any of the permissions is equivalent to
requesting full Bluetooth control.

Task-number: QTBUG-109964
Change-Id: Ie7ac6577cf6a21419b73b33f8cf7e87bc3f8cf43
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Ivan Solovev 2023-04-03 13:00:59 +02:00
parent 346dcc696b
commit 0198a74b72
3 changed files with 90 additions and 7 deletions

View File

@ -387,9 +387,60 @@ QT_PERMISSION_IMPL_COMMON(QMicrophonePermission)
*/
QT_PERMISSION_IMPL_COMMON(QBluetoothPermission)
: u{} // stateless, atm
: u{ShortData{CommunicationMode::Default, {}}}
{}
/*!
\enum QBluetoothPermission::CommunicationMode
\since 6.6
This enum is used to control the allowed Bluetooth communication modes.
\value Access Allow this device to access other Bluetooth devices. This
includes scanning for nearby devices and connecting to them.
\value Advertise Allow other Bluetooth devices to discover this device.
\value Default This configuration is used by default.
\note The fine-grained permissions are currently supported only on
Android 12 and newer. On older Android versions, as well as on Apple
operating systems, any mode results in full Bluetooth access.
\note For now the \c Access mode on Android also requests the
\l {QLocationPermission::Precise}{precise location} permission.
This permission coupling may change in the future.
*/
/*!
\since 6.6
Sets the allowed Bluetooth communication modes to \a modes.
\note A default-constructed instance of \l {QBluetoothPermission::}
{CommunicationModes} has no sense, so an attempt to set such a mode will
raise a \c {qWarning()} and fall back to using the
\l {QBluetoothPermission::}{Default} mode.
*/
void QBluetoothPermission::setCommunicationModes(CommunicationModes modes)
{
if (modes == CommunicationModes{}) {
qCWarning(lcPermissions, "QBluetoothPermission: trying to set an invalid empty mode. "
"Falling back to CommunicationMode::Default.");
u.data.mode = Default;
} else {
u.data.mode = static_cast<CommunicationMode>(modes.toInt());
}
}
/*!
\since 6.6
Returns the allowed Bluetooth communication modes.
*/
QBluetoothPermission::CommunicationModes QBluetoothPermission::communicationModes() const
{
return u.data.mode;
}
/*!
\class QLocationPermission
\brief Access the user's location.

View File

@ -173,6 +173,32 @@ private:
};
Q_DECLARE_SHARED(QContactsPermission)
class QBluetoothPermissionPrivate;
class QBluetoothPermission
{
Q_GADGET_EXPORT(Q_CORE_EXPORT)
public:
enum CommunicationMode : quint8 {
Access = 0x01,
Advertise = 0x02,
Default = Access | Advertise,
};
Q_DECLARE_FLAGS(CommunicationModes, CommunicationMode)
Q_FLAG(CommunicationModes)
Q_CORE_EXPORT void setCommunicationModes(CommunicationModes modes);
Q_CORE_EXPORT CommunicationModes communicationModes() const;
private:
struct ShortData {
CommunicationMode mode;
char reserved[sizeof(void*) - sizeof(mode)];
};
QT_PERMISSION(QBluetoothPermission)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QBluetoothPermission::CommunicationModes)
Q_DECLARE_SHARED(QBluetoothPermission)
#define Q_DECLARE_MINIMAL_PERMISSION(ClassName) \
class ClassName##Private; \
class ClassName \
@ -184,7 +210,6 @@ Q_DECLARE_SHARED(QContactsPermission)
Q_DECLARE_MINIMAL_PERMISSION(QCameraPermission)
Q_DECLARE_MINIMAL_PERMISSION(QMicrophonePermission)
Q_DECLARE_MINIMAL_PERMISSION(QBluetoothPermission)
#undef QT_PERMISSION
#undef Q_DECLARE_MINIMAL_PERMISSION

View File

@ -49,7 +49,7 @@ static QStringList nativeLocationPermission(const QLocationPermission &permissio
return nativeLocationPermissionList;
}
static QStringList nativeBluetoothPermission()
static QStringList nativeBluetoothPermission(const QBluetoothPermission &permission)
{
// See https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
// for the details.
@ -64,10 +64,17 @@ static QStringList nativeBluetoothPermission()
// strictly necessary for API Level >= 31. See QTBUG-112164.
static QString fineLocation = u"android.permission.ACCESS_FINE_LOCATION"_s;
if (QtAndroidPrivate::androidSdkVersion() < 31)
if (QtAndroidPrivate::androidSdkVersion() < 31) {
return {bluetoothGeneral, fineLocation};
else
return {bluetoothScan, bluetoothAdvertise, bluetoothConnect, fineLocation};
} else {
const auto modes = permission.communicationModes();
QStringList permissionList;
if (modes & QBluetoothPermission::Advertise)
permissionList << bluetoothAdvertise;
if (modes & QBluetoothPermission::Access)
permissionList << bluetoothScan << bluetoothConnect << fineLocation;
return permissionList;
}
}
static QStringList nativeStringsFromPermission(const QPermission &permission)
@ -80,7 +87,7 @@ static QStringList nativeStringsFromPermission(const QPermission &permission)
} else if (id == qMetaTypeId<QMicrophonePermission>()) {
return { u"android.permission.RECORD_AUDIO"_s };
} else if (id == qMetaTypeId<QBluetoothPermission>()) {
return nativeBluetoothPermission();
return nativeBluetoothPermission(*permission.value<QBluetoothPermission>());
} else if (id == qMetaTypeId<QContactsPermission>()) {
const auto readContactsString = u"android.permission.READ_CONTACTS"_s;
switch (permission.value<QContactsPermission>()->accessMode()) {