Bluetooth Low Energy

Can a Bluetooth LE app run in the background on Android?

Yes, as long as the app process stays alive or the system can wake it. Android lets a BLE app scan, connect and stay connected while it is not visible, but the process must be running, so apps use a foreground service of type connectedDevice, a filtered scan that wakes the app through a PendingIntent, or the Companion Device Manager for a paired accessory. Unfiltered scans stop when the screen turns off, and apps that target Android 12 or higher cannot start a foreground service from the background except in listed cases.

TL;DR

An Android BLE app works off screen only while its process is alive or the system can wake it. Pick one mechanism on purpose: a connectedDevice foreground service, a PendingIntent scan or the Companion Device Manager. Always scan with a filter and expect the process to be killed anyway. Design sync around gaps, with numbered records written idempotently.

Learning objectives

After reading this article you will be able to:

  • Choose between a connectedDevice foreground service, a PendingIntent scan and the Companion Device Manager for background BLE work
  • Explain why unfiltered scans stop at screen off and what Android 12 and 14 changed for foreground services
  • Design a sync between a phone and a wearable or sensor that it only sees now and then

Android background BLE mechanisms

Android lets a BLE app scan, connect and stay connected while it is not on screen, as long as the app process is alive. Android’s guide to background communication says there is no limitation on using the scanning or Companion Device Manager APIs while the app is not visible, but both need the process to be running. The work is choosing how the process stays alive, or how the system wakes it.

MechanismAndroid versionsFind devicesConnectStay connected
Filtered scan with a ScanCallbackAll with BLEYes, while the process runs; unfiltered scans pause at screen offYes, while the process runsUntil the process is killed
Scan with a PendingIntentAndroid 8.0 (API level 26) and laterYes, and the system wakes the app on a matchBriefly, from a Worker; on Android 11 and lower a foreground service can start directlyNo, short work only
Foreground service, type connectedDeviceType attribute from Android 10; required for apps targeting Android 14YesYesYes, while the service runs
Companion Device Manager with CompanionDeviceServiceAssociation from Android 8.0; presence service from Android 12; new presence request API in Android 16The system watches for the associated deviceYes, the app is bound when the device appearsYes, while the device is in range or connected
WorkManagerAnyNot suggested by the guideYes, for a defined jobNo, app restrictions apply

Rows reflect Android’s developer documentation as of October 2026. Advertising follows the same rule as the rest: it needs a running process, and the background guide does not treat it separately.

Two older rules still matter. On Android 10 and 11, a service that discovers Bluetooth devices must also hold ACCESS_BACKGROUND_LOCATION. And from Android 12, apps can drop location for scanning altogether, which Bluetooth permissions on Android, explained covers with the rest of the permission set.

Foreground services for background Bluetooth

A foreground service is needed when a link has to stay open while the user is elsewhere, such as syncing data or listening to a peripheral’s notifications. Android’s background guide names a foreground service of type connectedDevice as one of the two ways to keep a connection alive, the other being the Companion Device Manager. Shorter jobs can use a PendingIntent scan or WorkManager instead.

To run one:

  1. Declare the type. Add android:foregroundServiceType="connectedDevice" to the service and declare FOREGROUND_SERVICE_CONNECTED_DEVICE. Apps that target Android 14 must declare a type for every foreground service.
  2. Hold a Bluetooth permission first. A connectedDevice service has a runtime prerequisite, which a Bluetooth app meets with at least one granted permission among BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE and BLUETOOTH_SCAN.
  3. Start it while the app is visible. Apps that target Android 12 or higher cannot start a foreground service from the background, and the system throws ForegroundServiceStartNotAllowedException. Exemptions include the user acting on a notification or widget, a few system broadcasts, the user turning off battery optimisation for the app, and apps using the Companion Device Manager with one of its background permissions.
  4. Show the notification. The service carries a status bar notification so the user knows the work is running.
  5. Reconnect after a kill. A foreground service makes the process less likely to be killed, not immune. When a connection drops because the process died, reconnect using the steps Android gives for connecting to a device.

Android also says to keep connections only as long as needed and disconnect once the task is done, so a service that runs all day should have a reason to.

Background scanning without a filter

Android stops unfiltered scans when the screen turns off, to save power. The BluetoothLeScanner reference says scanning resumes when the screen turns back on, and that a scan with a ScanFilter avoids the pause. A background scan should always name what it is looking for, usually a service UUID or a manufacturer ID.

Android’s scanning guide adds two habits: stop scanning as soon as the device is found, and never scan on a loop without a time limit. For an app whose process is not always running, startScan() with a PendingIntent, available from Android 8.0 (API level 26), hands the scan to the system and delivers matching results to the app, which wakes to receive them. The background guide discourages scheduling periodic scans instead, because that starts the app whether or not a device is in range.

Companion Device Manager for wearables

The Companion Device Manager (CDM) is Android’s system service for associating an app with a specific accessory, such as a watch or a sensor, and then keeping that app awake while the accessory is near. Use it when the app serves one known device that the user pairs once, which is the usual shape of a wearable app.

How it works:

  • Association. From Android 8.0, the app asks the CDM to find devices that match a filter. The system scans on the app’s behalf, shows the user a chooser, and the app does not need ACCESS_FINE_LOCATION for that scan, though Location Services must be on. Association alone opens no connection and does not scan continuously.
  • Background rights. An associated app can declare REQUEST_COMPANION_RUN_IN_BACKGROUND, which lets it run in the background and start a foreground service from there, or the narrower REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND.
  • Presence. The app implements a CompanionDeviceService and asks the CDM to observe the device’s presence. The system binds the service when the device comes into Bluetooth LE range or connects over Bluetooth, and unbinds it when the device leaves. Binding raises the app’s process priority. Android 16 replaced the address-based startObservingDevicePresence(String) with a version that takes an ObservingDevicePresenceRequest.

The limits are in Android’s own guide: limited filtering, and no support for random MAC addresses. The CDM reference recommends that a device with a rotating address use a resolvable private address and be bonded to the phone so Android can resolve it. If your peripheral cannot do that, use a filtered scan and a foreground service instead.

Android GATT error 133

Status 133 is a generic failure from Android’s Bluetooth stack, so it says that a connection or operation failed but not why. 133 is 0x85, which the open-source Android Bluetooth stack defines as GATT_ERROR. It is not one of the status constants in the public BluetoothGatt reference, which lists codes such as GATT_INSUFFICIENT_AUTHENTICATION (5) and GATT_FAILURE (257).

Android does not document the causes of 133, so treat it as a prompt to rule out the connection rules Android does document:

  1. Close what you are finished with. Android’s GATT guide says to call close() on the BluetoothGatt object when you are done with a connection. Close the old object before starting a new attempt to the same device.
  2. Ask for the LE transport. For a device that supports both Bluetooth Classic and LE, pass BluetoothDevice.TRANSPORT_LE to connectGatt(), which takes a preferred transport for dual-mode devices.
  3. Do not overlap connection requests. Android’s background guide says versions below Android 10 allow one connection request at a time and queue the rest. Run one GATT operation and one connection attempt at a time per device.
  4. Check the peripheral is there. A direct connection, with autoConnect false, fails if the device is not available. For a device that comes and goes, autoConnect true makes Android try to reconnect whenever the device is available again.
  5. Get a fresh device handle. Take the BluetoothDevice from a current scan result, the bonded device list or getRemoteLeDevice().

Then retry with a delay rather than in a tight loop, and log the device model and Android version beside each failure, because behaviour differs between phones.

Intermittent wearable and sensor sync

A wearable or sensor that only sees the phone now and then needs sync designed so that missing the phone for hours loses nothing and reconnecting sends each record exactly once. Android’s background rules mean the phone will not always be listening, so the device has to hold its data and the protocol has to survive gaps.

  1. Store on the device while apart. The wearable or sensor writes each reading to its own flash with a sequence number that only goes up, so it never depends on the phone being there.
  2. Batch on reconnect. When the link comes back, send everything since the last acknowledged sequence number in as few transfers as possible. Why does batching save battery and bandwidth? explains the saving.
  3. Use sequence numbers to find gaps. The phone records the highest sequence number it has stored for each device and asks for anything missing, so a dropped connection mid-transfer resumes rather than restarts.
  4. Make writes idempotent. Key each record by device and sequence number, so a record sent twice after a retry is stored once. What is idempotency? covers the pattern.
  5. Acknowledge after storing. The phone acknowledges only after the record is durably written, and the device deletes only acknowledged records.
  6. Carry the device’s own time. Stamp readings with the device clock and the sequence number, and correct for drift on the phone. What is clock drift? explains why device clocks disagree.
  7. Let the system wake the app. On Android, use the Companion Device Manager’s presence service or a filtered PendingIntent scan so the app wakes when the device appears, rather than scanning on a timer.

iOS vs Android background Bluetooth rules

Both platforms allow background Bluetooth LE work under conditions, but they set different conditions. Android cares whether the process is alive and how it was started. iOS cares whether the app declared a Bluetooth background mode, and limits what it can do once it has.

AndroidiOS
How background work is allowedA live process: foreground service, PendingIntent scan, or Companion Device ManagerThe bluetooth-central or bluetooth-peripheral background mode
Background scanningFiltered scans only stay on at screen offScans must list service UUIDs and run less often
Background advertisingNeeds a running processService UUIDs move to an overflow area and the local name is dropped
After the app is killedPendingIntent scans and the Companion Device Manager can wake itState preservation and restoration relaunches it for Bluetooth events
User-visible signForeground service notificationNo notification required

Can a BLE app run in the background on iOS? covers the iOS column in detail. Test every combination of foreground and background, on both platforms, on real phones.

Offline Protocol and Android background limits

Offline Protocol’s React Native SDK ships the Bluetooth LE bridges for iOS and Android, and it lives under these same rules, so it cannot keep a mesh running anywhere the operating system would not keep any other app running. On Android its manifest declares a foreground service of type connectedDevice, shown to the user as a “Mesh Active” notification with a Stop action. The SDK’s documentation states that Android can still kill the process, and that by default the SDK does not recreate the protocol on its own afterwards, because a protocol running with no app code to store incoming messages would acknowledge messages that are then lost. Apps reconcile mesh state when they come back to the foreground, or opt in to having the app’s own code restart it. A preview Kotlin library for native Android apps is also available.

For wearables, the design is the one in the sync section above. The wearable keeps its own Bluetooth LE link to its companion app, and the SDK runs on the phone, where it can carry that data to other phones nearby or to a backend once a path exists.

Frequently asked questions

Does Android stop BLE scanning when the screen turns off?

Only for unfiltered scans. The BluetoothLeScanner reference says unfiltered scans stop at screen off to save power and resume when the screen turns on, and that a scan with a ScanFilter avoids this.

Which foreground service type does a BLE app use on Android 14?

connectedDevice, declared with the FOREGROUND_SERVICE_CONNECTED_DEVICE permission. Apps that target Android 14 must declare a type for every foreground service, and a connectedDevice service needs a Bluetooth runtime permission such as BLUETOOTH_CONNECT before it starts.

Can a PendingIntent scan start a foreground service on Android 12?

Not directly for apps that target Android 12 or higher. Android's background guide says to start a Worker and a Job from the PendingIntent instead, which suits short communication, and that below Android 12 the app could start a foreground service straight from it.

Do I need location permission for background BLE scanning on Android?

On Android 10 and 11, a service that discovers Bluetooth devices also needs ACCESS_BACKGROUND_LOCATION. From Android 12, an app can assert neverForLocation on BLUETOOTH_SCAN and skip location, though some beacons are then filtered from results.

Does the Companion Device Manager work with devices that rotate their address?

Android's background guide lists no support for random MAC addresses as a limit. The CompanionDeviceManager reference recommends that a device with a rotating address use a resolvable private address and be bonded to the phone, so Android can resolve it.

Is GATT error 133 a documented Android error code?

Not in the public API reference. 133 is 0x85, which Android's open-source Bluetooth stack names GATT_ERROR, a generic failure, so the code alone does not say what went wrong.

Sources

Build it with Offline Protocol

The React Native installation guide lists the Android permissions and the foreground service the mesh SDK declares, the runtime permissions your app must request, and the iOS background modes it needs.

Read the React Native installation guide