Request only the Bluetooth permission the app actually uses

Lint flagged ACCESS_FINE_LOCATION declared without ACCESS_COARSE_LOCATION,
which Android 12 requires to be requested together.

Rather than add the missing one, drop location entirely: the Bluetooth
transport only talks to already-paired devices, enumerating bonded
devices and opening an RFCOMM socket. Neither is a scan, so no location
access is needed. BLUETOOTH_SCAN and BLUETOOTH_ADVERTISE go for the same
reason — nothing was using them.

Discovery of unpaired devices would bring all of these back; the manifest
notes what that would require.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qjfMiPUDshdKEUa6vxqzN
This commit is contained in:
Claude
2026-08-05 22:56:27 +00:00
parent 4ad0f52331
commit 85ec10836b
2 changed files with 13 additions and 20 deletions
+10 -13
View File
@@ -10,25 +10,22 @@
<!-- Lets the app hand a downloaded APK to the system installer. --> <!-- Lets the app hand a downloaded APK to the system installer. -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- Bluetooth. Android 12 (API 31) split the old catch-all permissions into <!-- Bluetooth. Android 12 (API 31) replaced the catch-all BLUETOOTH
explicit SCAN/CONNECT/ADVERTISE, so the legacy pair is capped below 31 permission with explicit ones, so the legacy pair is capped below 31
and the new trio is requested at runtime above it. neverForLocation and BLUETOOTH_CONNECT is requested at runtime above it.
tells the system we aren't deriving physical location from scans, which
avoids dragging in the location permission on 12+. --> Only CONNECT is declared because the transport talks exclusively to
devices that are already paired: it enumerates bonded devices and
opens an RFCOMM socket, neither of which is a scan. Adding discovery
of unpaired devices later would mean declaring BLUETOOTH_SCAN here
(and, below API 31, both ACCESS_FINE_LOCATION and
ACCESS_COARSE_LOCATION, which Android requires together). -->
<uses-permission android:name="android.permission.BLUETOOTH" <uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" /> android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" /> android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
tools:targetApi="s" /> tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"
tools:targetApi="s" />
<!-- Pre-31, Bluetooth discovery is gated behind location access. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<!-- Both radios are optional: the app is fully playable against AI without them. --> <!-- Both radios are optional: the app is fully playable against AI without them. -->
<uses-feature android:name="android.hardware.bluetooth" android:required="false" /> <uses-feature android:name="android.hardware.bluetooth" android:required="false" />
@@ -47,14 +47,10 @@ class MainActivity : ComponentActivity() {
} }
private fun requestBluetoothPermissions() { private fun requestBluetoothPermissions() {
// Only CONNECT: the transport reaches paired devices and never scans.
// See the manifest for what discovery of unpaired devices would add.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
bluetoothPermissions.launch( bluetoothPermissions.launch(arrayOf(Manifest.permission.BLUETOOTH_CONNECT))
arrayOf(
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_ADVERTISE,
)
)
} }
} }