New Bluetooth permissions in Android 12

In this article, I will discuss the new Bluetooth permissions introduced in Android 12.

New permissions

For apps targeting Android 12 or higher, the following three new permissions are introduced:

  1. BLUETOOTH_CONNECT: required to connect to paired Bluetooth devices.
  2. BLUETOOTH_SCAN: equired to scan and pair nearby Bluetooth devices.
  3. BLUETOOTH_ADVERTISE: required to advertise to nearby Bluetooth devices.

For apps that also support older Android versions, we can declare the permissions like this:

<manifest ...>
    <!-- for Android 11 and lower -->
    <uses-permission
        android:name="android.permission.BLUETOOTH"
        android:maxSdkVersion="30" />
    <uses-permission
        android:name="android.permission.BLUETOOTH_ADMIN"
        android:maxSdkVersion="30" />
    <!-- needed only for scanning -->
    <uses-permission
        android:name="android.permission.ACCESS_FINE_LOCATION"
        android:maxSdkVersion="30" />

    <!-- for Android 12 and above, include the ones you really need -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

    ...
</manifest>

No location permission?

Really? No location permission required now? Well, yes and no.

If the app uses Bluetooth to derive physical location, e.g. through BLE beacons, you still need to declare the ACCESS_FINE_LOCATION permission as before.

However, if the app does not derive physical locations, you can add the android:usesPermissionFlags="neverForLocation" attribute to the BLUETOOTH_SCAN permission declaration:

<manifest ...>
    <uses-permission
        android:name="android.permission.BLUETOOTH_SCAN"
        android:usesPermissionFlags="neverForLocation" />

    ...
</manifest>

Request user approval

Unfortunately, when you run the app on Android 12 devices, you will get the SecurityException for missing the needed permissions. That’s because the new permissions are runtime permissions that must be approved by the user.

Assume the app doesn’t need to advertise, and doesn’t derive physical locations, we can request the permission like this:

class MainActivity : Activity() {
    companion object {
        private const val BLUETOOTH_PERMISSION_REQUEST_CODE = 9999
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        initializeBluetoothOrRequestPermission()

        ...
    }

    private fun initializeBluetoothOrRequestPermission() {
        val requiredPermissions = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
            listOf(Manifest.permission.ACCESS_FINE_LOCATION)
        } else {
            listOf(Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN)
        }

        val missingPermissions = requiredPermissions.filter { permission ->
            checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED
        }
        if (missingPermissions.isEmpty()) {
            initializeBluetooth()
        } else {
            requestPermissions(missingPermissions.toTypedArray(), BLUETOOTH_PERMISSION_REQUEST_CODE)
        }
    }

    private fun initializeBluetooth() { ... }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        when (requestCode) {
            BLUETOOTH_PERMISSION_REQUEST_CODE -> {
                if (grantResults.none { it != PackageManager.PERMISSION_GRANTED }) {
                    // all permissions are granted
                    initializeBluetooth()
                } else {
                    // some permissions are not granted
                }
            }
            else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
    }

    ...
} 

Conclusion

This is all you need to use the new Bluetooth permissions for Android 12 and above, and it’s really a great improvement for no longer requiring the location permission for scanning.

Let me know how if you have any questions and happy coding!


See also

comments powered by Disqus