'Android 12 - Launching Foreground Service from a background Bluetooth LE scan BroadcastReceiver throws ForegroundServiceStartNotAllowedException

Edit: I believe this to be a bug in AOSP. Please would everyone star this issue so it gets more attention.


My app records the user's trip while driving a vehicle using a Foreground Service to capture GPS. It's intended to be triggered by the presence of a BLE beacon (or Activity Recognition). This app is designed to run while the app is closed and works fine when targeting API 30, but fails with API 31 (Android 12).

Since targeting API 31 it's being caught by the new Background Start Restrictions in Android 12 - when attempting to start the service as a result of a BLE scan result, I now get: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.example.myapp/com.example.myappsdk.trip.RecordingService

However, both of my use cases for triggering the Foreground Service from the background are Permitted Exemptions:

  • Your app receives a Bluetooth broadcast that requires the BLUETOOTH_CONNECT or BLUETOOTH_SCAN permissions.
  • Your app receives an event that's related to geofencing or activity recognition transition.

...so I can't understand why this exception is being thrown. The documentation does not describe any particular steps required to make it work.

This is the code to trigger the BLE scan, it's called by the Application object. At this point the user has explicitly granted the android.permission.BLUETOOTH_SCAN permission at runtime:

 BluetoothManager btManager = (BluetoothManager) (appContext.getSystemService(Context.BLUETOOTH_SERVICE));
 ScanSettings settings = new ScanSettings.Builder()
    .setLegacy(false)
    .setScanMode(SCAN_MODE_LOW_LATENCY)
    .setUseHardwareBatchingIfSupported(true)
    .setUseHardwareFilteringIfSupported(true)
    .setReportDelay(5000)
    .build();
 
 List<ScanFilter> filters = new ArrayList<>();
 filters.add(new ScanFilter.Builder().setDeviceAddress("AB:CD:EF:01:23:45").build());
 
 Intent intent = new Intent(appContext, BackgroundScanResultReceiver.class);
 intent.setAction("com.example.BACKGROUND_SCAN_RESULT");
 int flags;
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
      // Must be mutable to allow system to add intent extras
    flags = PendingIntent.FLAG_UPDATE_CURRENT|PendingIntent.FLAG_MUTABLE; 
 else
    flags = PendingIntent.FLAG_UPDATE_CURRENT;
 PendingIntent pi = PendingIntent.getBroadcast(appContext, 1, intent, flags);
 
 BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
 scanner.startScan(filters, settings, appContext, pi);

This is the ResultReceiver which receives the scan results and starts the service:

public class BackgroundScanResultReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {        
        List<ScanResult> scanResults = intent.getParcelableArrayListExtra(BluetoothLeScannerCompat.EXTRA_LIST_SCAN_RESULT);        
        for (ScanResult result : scanResults) {
            BluetoothDevice btDevice = result.getDevice();
            if (!btDevice.getAddress().equals("AB:CD:EF:01:23:45")) {
                return;
            }
            Intent recordIntent = new Intent(context, RecordingService.class);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context.startForegroundService(recordIntent); // <-- Exception thrown here when app in Background
            } else {
                context.startService(recordIntent);
            }
        }
    }
}

The RecordingService is declared in the manifest like so:

<service
    android:name=".trip.RecordingService"
    android:description="@string/recording_service_description"
    android:enabled="true"
    android:exported="false"
    android:foregroundServiceType="location"
    android:label="@string/recording_service_label" />

BroadcastReceiver defined in the manifest as simply:

<receiver android:name=".beacon.BackgroundScanResultReceiver"/>

For anyone who may wish to recommend using WorkManager instead, I would point out that Android still recommends using Foreground Services over WorkManager in certain use cases including "Activity Tracking".

My BLE scanning is handled by Nordic's Android-Scanner-Compat-Library, but on O+ it's simply a wrapper for the native API. I've tried directly swapping it for the native BluetoothLeScanner which made no difference.

It appears I'm not the only one encountering this exception in supposedly permitted cases: How to use activity recognition Exemptions to start foregroundService from background?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source