Bluetooth Low Energy

Bluetooth permissions on Android, explained

An Android app that targets Android 12 or higher declares BLUETOOTH_SCAN to look for devices, BLUETOOTH_ADVERTISE to make the phone discoverable and BLUETOOTH_CONNECT to talk to paired devices, and asks the user for them at runtime as the Nearby devices permission. On Android 11 and lower, scanning needs the location permission instead, because scan results could reveal where the user is.

Learning objectives

After reading this article you will be able to:

  • Identify what BLUETOOTH_SCAN, BLUETOOTH_ADVERTISE and BLUETOOTH_CONNECT each cover
  • Explain why older Android versions need location for scanning and what neverForLocation changes
  • Describe the foreground service setup a BLE app needs to keep working in the background

Why Android asks for Bluetooth permissions

Bluetooth lets an app find, identify and talk to devices around the user, and Android treats that as sensitive. An app declares the Bluetooth permissions it needs in its manifest, and some of them must also be granted by the user while the app runs. Which ones depends on the Android version the app targets, and the rules changed sharply with Android 12 (API level 31).

The app can also state whether it needs Bluetooth at all. Declaring android.hardware.bluetooth_le with required="true" hides the app on Google Play from devices without Bluetooth LE. With required="false", the app installs everywhere and checks for the feature at runtime with PackageManager.hasSystemFeature().

Android 12 and higher: three permissions

An app that targets Android 12 or higher declares one permission per kind of Bluetooth activity:

PermissionWhat it covers
BLUETOOTH_SCANLooking for Bluetooth devices, such as BLE peripherals
BLUETOOTH_ADVERTISEMaking this device discoverable to other Bluetooth devices
BLUETOOTH_CONNECTCommunicating with Bluetooth devices that are already paired

All three are runtime permissions. The app must ask the user before it scans, advertises or communicates, and when it requests any of them the system shows a single prompt to allow access to Nearby devices.

An app that only connects to an accessory may need just BLUETOOTH_SCAN and BLUETOOTH_CONNECT. An app that links two phones runs both BLE roles, scanning on one side and advertising on the other, so it asks for all three.

Android’s guide also says to keep the legacy BLUETOOTH and BLUETOOTH_ADMIN declarations for older devices but cap them with android:maxSdkVersion="30", so that Android 12 and higher grant only the new permissions.

Location and neverForLocation

Before Android 12, scanning for Bluetooth devices required the location permission. Android’s reasoning is that on Android 11 and lower a Bluetooth scan could be used to gather information about the user’s location. So an app targeting Android 11 or lower declares BLUETOOTH, BLUETOOTH_ADMIN (to start device discovery) and ACCESS_FINE_LOCATION, and requests the location permission at runtime. An app with a service that can run on Android 10 or 11 must also declare ACCESS_BACKGROUND_LOCATION to discover Bluetooth devices.

From Android 12, location is needed only if the app uses Bluetooth scan results to derive physical location. An app that does not can say so firmly:

  1. Add android:usesPermissionFlags="neverForLocation" to its BLUETOOTH_SCAN declaration.
  2. Remove ACCESS_FINE_LOCATION, or set android:maxSdkVersion="30" on it so it still applies to older devices.

The assertion has a cost. Android notes that with neverForLocation, some BLE beacons are filtered from scan results, and the BluetoothLeScanner reference says it may restrict the types of devices the app can interact with. An app that works with BLE beacons should check that the beacons it needs still appear.

Background work and foreground services

Permissions decide what an app may do. Android’s background rules decide when. A BLE app that has to keep scanning, advertising or holding connections while the user is in another app usually runs a foreground service, which shows a status bar notification so the user knows the work is happening.

  • Declare the type. For apps targeting API level 34 or higher, each foreground service is declared with an android:foregroundServiceType, and starting one whose type is not declared throws MissingForegroundServiceTypeException. Bluetooth work uses connectedDevice, with the FOREGROUND_SERVICE_CONNECTED_DEVICE permission.
  • Hold a Bluetooth permission first. A connectedDevice service has a runtime prerequisite, which Bluetooth apps meet by being granted at least one of BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE or BLUETOOTH_SCAN.
  • Ask to post notifications. From Android 13, POST_NOTIFICATIONS is a runtime permission. An app does not need it to start a foreground service, but if the user denies it, the service’s notice appears only in the Task Manager, not in the notification drawer.
  • Filter scans. The BluetoothLeScanner reference says unfiltered scans stop when the screen turns off and resume when it turns on. A scan with a ScanFilter avoids this.
  • Declare it in Play Console. Apps targeting Android 14 or higher also declare their foreground service types in the Play Console.

Android’s background BLE guide adds one more option: calling startScan() with a PendingIntent lets the system wake the app when a matching device is seen, instead of keeping the process alive to scan.

Putting it together

For an app that links two phones over BLE, the usual checklist is:

  1. Declare the three Android 12 permissions, with neverForLocation on BLUETOOTH_SCAN if the app never derives location.
  2. Keep BLUETOOTH, BLUETOOTH_ADMIN and ACCESS_FINE_LOCATION capped at maxSdkVersion="30" for older devices.
  3. At runtime, request the Nearby devices permissions on Android 12 and higher, or location on Android 11 and lower, plus notifications on Android 13 and higher.
  4. Declare a connectedDevice foreground service if the link must survive the app leaving the screen.

Offline Protocol’s React Native mesh SDK is a concrete example: its installation guide says the SDK’s own manifest declares the Bluetooth and foreground service permissions, while the app adds location for Android 11 and lower and POST_NOTIFICATIONS, and must request the runtime permissions itself, because the SDK checks them but never asks. iOS works differently, and Can a BLE app run in the background on iOS? covers that side.

Frequently asked questions

Does a BLE app on Android 12 or higher still need location permission?

Only if it uses Bluetooth scan results to work out where the device is, or needs location for something else. Otherwise it can add neverForLocation to BLUETOOTH_SCAN and limit ACCESS_FINE_LOCATION to Android 11 and lower with maxSdkVersion 30. Android notes that neverForLocation filters some BLE beacons from scan results.

Is there a way to connect to an accessory without these permissions?

For companion devices, Android 8.0 and higher offer the Companion Device Manager, which shows a pairing interface on the app's behalf and, according to Android's documentation, does not require location permissions. Android's background BLE guide notes its limits, including limited filtering and no support for random MAC addresses.

Sources

Build it with Offline Protocol

The React Native installation guide lists the Android permissions the mesh SDK's manifest already declares, the ones your app must add, and a runtime request function that chooses the right set by API level.

Read the React Native installation guide