Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/2522.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Permission disabling is now handled correctly
1 change: 1 addition & 0 deletions changes/2522.feature.md
Comment thread
depaolim marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the ability to specify bluetooth permission.
4 changes: 4 additions & 0 deletions docs/en/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,10 @@ Applications may also need to declare the permissions they require. Permissions

Briefcase maintains a set of cross-platform permissions:

#### `permission.bluetooth`

Permission to connect to an external device via Bluetooth.
Comment thread
depaolim marked this conversation as resolved.

#### `permission.camera`

Permission to access the camera to take photos or video.
Expand Down
30 changes: 28 additions & 2 deletions docs/en/reference/platforms/android/gradle.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ A property whose sub-properties define the platform-specific permissions that wi

For example, specifying:

```python
permission."android.permission.HIGH_SAMPLING_RATE_SENSORS" = true
```toml
permission."android.permission.HIGH_SAMPLING_RATE_SENSORS" = {}
```

will result in an `AndroidManifest.xml` declaration of:
Expand All @@ -249,6 +249,22 @@ will result in an `AndroidManifest.xml` declaration of:
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS">
```

Using a dictionary as a value allows you to specify additional attributes that detail, for example, the API version or combined constraints.

For example, specifying:

```toml
permission."android.permission.BLUETOOTH_ADMIN" = {"android:maxSdkVersion"= "30"}
permission."android.permission.BLUETOOTH_SCAN" = {"android:usesPermissionFlags"= "neverForLocation"}
```

will result in an `AndroidManifest.xml` declaration of:

```xml
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
```

### `target_os_version`

The API level that the app will target. This controls the version of the Android SDK that is used to build your app (by setting the `compileSdkVersion` for your app), and the forwards compatibility behavioral changes your app will enable (by setting the `targetSdkVersion` setting). This is *not* the Android version; it is the underlying API level. For example, Android 15 uses an API level of 35; if you wanted to specify Android 15 as your target API level, you would define `target_os_version = "35"`.
Expand All @@ -270,6 +286,7 @@ If you want to manually specify a version code by defining `version_code` in you

Briefcase cross platform permissions map to `<uses-permission>` declarations in the app's `AppManifest.xml`:

* [`permission.bluetooth`][permissionbluetooth]: `android.permission.BLUETOOTH` and other (see below for details)
* [`permission.camera`][permissioncamera]: `android.permission.CAMERA`
* [`permission.microphone`][permissioncamera]: `android.permission.RECORD_AUDIO`
* [`permission.coarse_location`][permissioncoarse_location]: `android.permission.ACCESS_COARSE_LOCATION`
Expand All @@ -279,6 +296,15 @@ Briefcase cross platform permissions map to `<uses-permission>` declarations in

Every application will be automatically granted the `android.permission.INTERNET` and `android.permission.NETWORK_STATE` permissions.

Specifying a [`permission.bluetooth`][permissionbluetooth] permission will result in the following `<uses-permission>` declarations in the app's `AppManifest.xml`:

* `android.permission.ACCESS_COARSE_LOCATION`, with an attribute declaration of `android:maxSdkVersion="30"`. If `permission.coarse_location` is defined, the attribute declaration will be omitted
* `android.permission.ACCESS_FINE_LOCATION`, with an attribute declaration of `android:maxSdkVersion="30"`. If `permission.coarse_location` is defined, the attribute declaration will be omitted
* `android.permission.BLUETOOTH`, with an attribute declaration of `android:maxSdkVersion="30"`
* `android.permission.BLUETOOTH_ADMIN"`, with an attribute declaration of `android:maxSdkVersion="30"`
* `android.permission.BLUETOOTH_CONNECT`
* `android.permission.BLUETOOTH_SCAN`, with an attribute declaration of `android:usesPermissionFlags="neverForLocation"`. If `permission.fine_location` or `permission.coarse_location` is defined, the attribute declaration will be omitted.

Specifying a [`permission.camera`][permissioncamera] permission will result in the following non-required [`feature`][] definitions being implicitly added to your app:

* `android.hardware.camera`,
Expand Down
1 change: 1 addition & 0 deletions docs/en/reference/platforms/iOS/xcode.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ The minimum iOS version that the app will support. This controls the value of `I

Briefcase cross-platform permissions map to the following [`info`][] keys:

* [`permission.bluetooth`][permissionbluetooth]: `NSBluetoothAlwaysUsageDescription`
* [`permission.camera`][permissioncamera]: `NSCameraUsageDescription`
* [`permission.microphone`][permissionmicrophone]: `NSMicrophoneUsageDescription`
* [`permission.coarse_location`][permissioncoarse_location]:
Expand Down
5 changes: 2 additions & 3 deletions docs/en/reference/platforms/macOS/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,11 @@ For more details on macOS document type declarations, see the following web page

## Permissions

### `macOS`

Briefcase cross platform permissions map to a combination of [`info`][] and [`entitlement`][] keys:

- [`permission.microphone`][permissionmicrophone]: an [`info`][] entry for `NSMicrophoneUsageDescription`; and an [`entitlement`][] of `com.apple.security.device.audio-input`
- [`permission.bluetooth`][permissionbluetooth]: an [`info`][] entry for `NSBluetoothAlwaysUsageDescription`; and an [`entitlement`][] of `com.apple.security.device.bluetooth`
- [`permission.camera`][permissioncamera]: an [`info`][] entry for `NSCameraUsageDescription`; and an [`entitlement`][] of `com.apple.security.device.camera`
- [`permission.microphone`][permissionmicrophone]: an [`info`][] entry for `NSMicrophoneUsageDescription`; and an [`entitlement`][] of `com.apple.security.device.audio-input`
- [`permission.coarse_location`][permissioncoarse_location]: an [`info`][] entry for `NSLocationUsageDescription` (ignored if [`permission.background_location`][permissionbackground_location] or [`permission.fine_location`][permissionfine_location] is defined); plus an entitlement of `com.apple.security.personal-information.location`
- [`permission.fine_location`][permissionfine_location]: an [`info`][] entry for `NSLocationUsageDescription`(ignored if [`permission.background_location`][permissionbackground_location] is defined); plus an [`entitlement`][] of `com.apple.security.personal-information.location`
- [`permission.background_location`][permissionbackground_location]: an [`info`][] entry for `NSLocationUsageDescription`; plus an [`entitlement`][] of `com.apple.security.personal-information.location`
Expand Down
1 change: 1 addition & 0 deletions src/briefcase/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def _x_permissions(self, app: AppConfig):
return {
key: app.permission.pop(key, None)
for key in [
"bluetooth",
"camera",
"microphone",
"coarse_location",
Expand Down
42 changes: 34 additions & 8 deletions src/briefcase/platforms/android/gradle.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,41 +239,67 @@ def permissions_context(self, app: AppConfig, x_permissions: dict[str, str]):
"""
# Default permissions for all Android apps
permissions = {
"android.permission.INTERNET": True,
"android.permission.ACCESS_NETWORK_STATE": True,
"android.permission.INTERNET": {},
"android.permission.ACCESS_NETWORK_STATE": {},
}

# Default feature usage for all Android apps
features = {}

if x_permissions["bluetooth"]:
permissions["android.permission.ACCESS_COARSE_LOCATION"] = {
"android:maxSdkVersion": "30"
}
permissions["android.permission.ACCESS_FINE_LOCATION"] = {
"android:maxSdkVersion": "30"
}
permissions["android.permission.BLUETOOTH"] = {
"android:maxSdkVersion": "30"
}
permissions["android.permission.BLUETOOTH_ADMIN"] = {
"android:maxSdkVersion": "30"
}
permissions["android.permission.BLUETOOTH_CONNECT"] = {}
permissions["android.permission.BLUETOOTH_SCAN"] = {
"android:usesPermissionFlags": "neverForLocation"
}

if x_permissions["camera"]:
permissions["android.permission.CAMERA"] = True
permissions["android.permission.CAMERA"] = {}
features["android.hardware.camera"] = False
features["android.hardware.camera.any"] = False
features["android.hardware.camera.front"] = False
features["android.hardware.camera.external"] = False
features["android.hardware.camera.autofocus"] = False

if x_permissions["microphone"]:
permissions["android.permission.RECORD_AUDIO"] = True
permissions["android.permission.RECORD_AUDIO"] = {}

if x_permissions["fine_location"]:
permissions["android.permission.ACCESS_FINE_LOCATION"] = True
permissions["android.permission.ACCESS_FINE_LOCATION"] = {}
Comment thread
depaolim marked this conversation as resolved.
features["android.hardware.location.network"] = False
features["android.hardware.location.gps"] = False
# We're good with the location. So we can also use BLUETOOTH_SCAN.
bt_scan_perm = permissions.get("android.permission.BLUETOOTH_SCAN")
if bt_scan_perm:
bt_scan_perm.pop("android:usesPermissionFlags", None)

if x_permissions["coarse_location"]:
permissions["android.permission.ACCESS_COARSE_LOCATION"] = True
permissions["android.permission.ACCESS_COARSE_LOCATION"] = {}
features["android.hardware.location.network"] = False
features["android.hardware.location.gps"] = False
# We're good with the location. So we can also use BLUETOOTH_SCAN.
bt_scan_perm = permissions.get("android.permission.BLUETOOTH_SCAN")
if bt_scan_perm:
bt_scan_perm.pop("android:usesPermissionFlags", None)

if x_permissions["background_location"]:
permissions["android.permission.ACCESS_BACKGROUND_LOCATION"] = True
permissions["android.permission.ACCESS_BACKGROUND_LOCATION"] = {}
features["android.hardware.location.network"] = False
features["android.hardware.location.gps"] = False

if x_permissions["photo_library"]:
permissions["android.permission.READ_MEDIA_VISUAL_USER_SELECTED"] = True
permissions["android.permission.READ_MEDIA_VISUAL_USER_SELECTED"] = {}

# Override any permission and entitlement definitions
# with the platform-specific definitions
Expand Down
2 changes: 2 additions & 0 deletions src/briefcase/platforms/iOS/xcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ def permissions_context(self, app: AppConfig, x_permissions: dict[str, str]):
# The collection of info.plist entries
info = {}

if x_permissions["bluetooth"]:
info["NSBluetoothAlwaysUsageDescription"] = x_permissions["bluetooth"]
if x_permissions["camera"]:
info["NSCameraUsageDescription"] = x_permissions["camera"]
if x_permissions["microphone"]:
Expand Down
3 changes: 3 additions & 0 deletions src/briefcase/platforms/macOS/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ def permissions_context(self, app: AppConfig, cross_platform: dict[str, str]):
"com.apple.security.cs.disable-library-validation": True,
}

if cross_platform["bluetooth"]:
entitlements["com.apple.security.device.bluetooth"] = True
info["NSBluetoothAlwaysUsageDescription"] = cross_platform["bluetooth"]
if cross_platform["camera"]:
entitlements["com.apple.security.device.camera"] = True
info["NSCameraUsageDescription"] = cross_platform["camera"]
Expand Down
Loading