Skip to content

[Bug]: addGeofence() in headless mode on Android reports success but OS Geofencer does not actively monitor the registration #1653

@johnswarbrick

Description

@johnswarbrick

Required Reading

  • Confirmed

Plugin Version

5.0.5

Flutter Doctor

[✓] Flutter (Channel stable, 3.41.2, on macOS 26.3 25D125 darwin-arm64, locale en-GB)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.3)
[✓] Chrome - develop for the web
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

Mobile operating-system(s)

  • iOS
  • Android

Device Manufacturer(s) and Model(s)

Google Pixel 8

Device operating-systems(s)

Android 16

What happened?

Expected Behavior

When addGeofence() is called from a headless task (app terminated), the newly registered geofence should be actively monitored by the Android OS Geofencer, and EXIT events should fire when the device leaves the geofence radius.

Actual Behavior

addGeofence() succeeds (returns without error, plugin logs ✅ SEEKERAPP_FINE) but the Android OS Geofencer logs "registration not active, registration not permitted" for the newly added geofence. The geofence is not actively monitored — EXIT events do not fire until the OS Geofencer's low-power batch evaluation eventually picks them up (~2–7 minutes later).

This creates a significant gap in geofence tracking reliability when the app is terminated.

Steps to Reproduce

  1. Configure plugin with stopOnTerminate: false, enableHeadless: true, geofenceModeHighAccuracy: false
  2. Start monitoring with startGeofences()
  3. Register initial geofences with addGeofence()
  4. Terminate the app (swipe kill)
  5. Move the device to trigger a geofence EXIT event
  6. In the headless task handler, call addGeofence() to register a new geofence at the updated location
  7. Continue moving — observe that EXIT events for the newly registered geofence are significantly delayed or missing

Debug Logs

Relevant logcat excerpts showing the issue:

Foreground geofences work correctly

16:26:13.717 TSLocationManager: ✅ SEEKERAPP_COARSE
16:26:13.718 TSLocationManager: ✅ SEEKERAPP_FINE
16:26:13.762 GeofenceProxy: Batched PI received for ...

After foreground addGeofence(), the OS Geofencer immediately shows "Batched PI received" confirming active monitoring.

App terminated, headless addGeofence() — OS rejects registration

16:27:14.504 TSLocationManager: ║ Geofencing Event: EXIT
16:27:14.504 TSLocationManager: ╟─ SEEKERAPP_FINE
16:27:58.508 [GeofenceHeadless] geofence EXIT: SEEKERAPP_FINE
16:27:59.065 TSLocationManager: ✅ SEEKERAPP_FINE   <-- addGeofence() reports success

16:28:00.017 GeofenceProxy: registration not active, registration not permitted.
16:28:00.017 GeofenceProxy: Batched PI received for ...   <-- note: this is evaluation of OLD registration

After headless addGeofence():

  • Plugin reports success ()
  • OS Geofencer logs "registration not active, registration not permitted"
  • No EXIT events fire for 7+ minutes until the OS Geofencer's low-power sweep picks up the registration

7-minute blackout period

16:28:00.017  Last "Batched PI" evaluation
...
16:35:02.024  Next "Batched PI" evaluation  (7 minutes later)

During this gap, the device continued moving through multiple geofence boundaries, but no EXIT events fired.

Root Cause Analysis

The issue appears to be that addGeofence() in headless mode updates the plugin's internal geofence state and calls the platform GeofencingClient.addGeofences() API, but the Android OS Geofencer does not honour the registration because the app's monitoring adapter is not in an "active" state.

The key evidence is the OS Geofencer log: "registration not active, registration not permitted". This indicates the OS acknowledges the addGeofences() API call but does not activate monitoring for it. The plugin, however, reports success because the API call itself did not throw an error.

Why startGeofences() cannot be called in headless mode

We initially tried calling startGeofences() after addGeofence() in the headless handler. However:

  1. startGeofences() internally calls stop() then start() (or equivalent)
  2. In headless mode, stop() kills the existing foreground monitoring session
  3. startGeofences() then fails with ForegroundServiceStartNotAllowedException on Android 12+ because background-launched processes cannot start foreground services
  4. Result: monitoring is permanently dead until the user opens the app

Our current workaround

We now call startGeofences() without stop() first:

// After addGeofence() in headless handler:
try {
  await bg.BackgroundGeolocation.startGeofences();
} catch (e) {
  // Expected on Android 12+ in headless mode.
  // No stop() was called, so existing monitoring is preserved.
}

This sometimes succeeds in refreshing the monitoring adapter. When it fails, the existing monitoring state is preserved (since we didn't call stop()). We also use the heartbeat callback as a fallback to detect and correct missed geofence transitions.

Suggested Fix

The plugin should detect when addGeofence() is called in headless mode and either:

  1. Automatically refresh the monitoring adapter after addGeofence() without a full stop/start cycle (similar to our workaround above)
  2. Return a warning or different status when the OS reports "registration not active" so callers know the geofence may not be actively monitored
  3. Document the limitation that addGeofence() in headless mode may not result in active monitoring on Android 12+

Context

We are building a location-aware mobile app that uses adjacent cells for geofencing. When a user exits one cell, we register a geofence for the new cell. This works perfectly in foreground/background but breaks when the app is terminated because the headless addGeofence() calls don't result in active OS monitoring.

The heartbeat callback (60s interval) serves as our fallback for detecting missed transitions, but the 7-minute blackout period after headless geofence registration significantly degrades the user experience.

Plugin Code and/or Config

bg.BackgroundGeolocation.ready(bg.Config(
  desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
  distanceFilter: 0,
  stopOnTerminate: false,
  enableHeadless: true,
  startOnBoot: true,
  heartbeatInterval: 60,
  preventSuspend: true,
  geofenceModeHighAccuracy: false,
  foregroundService: true,
));

bg.BackgroundGeolocation.startGeofences();

Relevant log output

See main report

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions