Skip to content

[Bug]: RemoteServiceException on LocationRequestService — Android 10 and below (Oppo/Vivo), Android 11+ unaffected #2627

Description

@vytechenterprise

Required Reading

  • Confirmed

Plugin Version

^5.1.1

Mobile operating-system(s)

  • iOS
  • Android

Device Manufacturer(s) and Model(s)

Oppo RMX1807 (confirmed reproducible on this model). Aggregate crash population across affected users: 73%
Oppo, 26% Vivo, <1% Xiaomi.

Device operating-systems(s)

Android 10 (73% of crash sessions), Android 8 (27% of crash sessions). Zero occurrences on Android 11+
across our full user base.

React Native / Expo version

React Native 0.81.5, Expo ~54.0.33

What do you require assistance about?

Fatal RemoteServiceException on LocationRequestService — Android 10 and below only (Oppo/Vivo)

Crash Details

Fatal Exception: android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground():
ServiceRecord{23860c4 u0
com.vytech.crmharbor/com.transistorsoft.locationmanager.service.LocationRequestService}

android.app.ActivityThread$H.handleMessage (ActivityThread.java:2161)
android.os.Handler.dispatchMessage (Handler.java:107)
android.os.Looper.loop (Looper.java:238)
android.app.ActivityThread.main (ActivityThread.java:7824)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:492)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:981)

Android requires Service.startForeground() to be called within ~5 seconds of
Context.startForegroundService(). On the affected device class, that window is being missed and the OS
fatally kills the app instead of the service simply failing to post its notification.

Native tslocationmanager version

4.2.+ (android/build.gradle)

Scope (Crashlytics data)

  • ~300 active users on this app.
  • All users on Android 11+ are completely unaffected — this signature does not appear on Android 11+ on
    any device manufacturer.
  • Affected: 3-4 devices (~1% of user base), all Android 10 or below.
  • Device breakdown among crashing sessions: 73% Oppo, 26% Vivo, <1% Xiaomi.
  • OS breakdown among crashing sessions: 73% Android 10, 27% Android 8.
  • 46% of crash occurrences happen while the app is in a background state.
  • Present across app versions 2.0.8, 2.0.9, and 2.0.10 — not a regression introduced by a recent release
    on our side.
Image Image Image

Steps to Reproduce

  1. Install app on an affected device class (confirmed on Oppo RMX1807, Android 10).
  2. Full uninstall → clean reinstall.
  3. Grant location permission "Allow all the time."
  4. Disable battery optimization for the app (standard Android exemption, via system settings).
  5. Enable GPS/location services.
  6. Attempt to fetch current location while app is backgrounded.
  7. Crash reproduces with the exact signature above.

Notably, this reproduces even with every relevant Android permission and the standard battery-optimization
exemption correctly granted — so this does not appear to be a permissions-configuration issue on our end.

Possibly related: #2599

We found #2599 (same exception class: RemoteServiceException$ForegroundServiceDidNotStartInTimeException,
also Android, still open). In that thread you noted these errors are "typically caused by [the reporter's]
own code blocking the main thread / preventing the SDK from doing its required work," and asked why they
called .watchPosition on app start.

We audited our own code for the same class of issue and want to flag a candidate trigger on our side, in
case it's relevant to your root-cause analysis:

getCurrentPosition() called from inside our headless enabledchange handler, fired the instant native
tracking reports enabled:false while the app is killed:

// helper/BackgroundLocationService.js — handleHeadlessEvent()
case "enabledchange": {
const isEnabled = event.params?.enabled;
if (!isEnabled) {
await BackgroundGeolocation.getCurrentPosition({
distanceFilter: 10,
samples: 1,
persist: true,
extras: { event: "enabled_change", meta: { enabled: false }, app_state: APP_STATE.KILLED },
});
// ...then sync()
}
break;
}
We use this to capture a final location fix at check-out time. It runs inside the plugin's own headless JS
context — the most constrained execution environment BGGeo offers — and immediately issues a
getCurrentPosition() call, which we understand invokes LocationRequestService natively. On an already
OEM-throttled process (Oppo/Vivo, Android 10), this seems like a plausible way to land inside the 5s
startForeground() window and miss it. Note that our surrounding try/catch does not catch this, since
RemoteServiceException is a fatal OS-level kill, not a JS promise rejection.

We haven't changed this yet since we wanted your input first — is this a legitimate risk factor, and if
so, is there a safer pattern (e.g. avoiding getCurrentPosition() inside a headless handler, or gating it
by platform/OEM) that you'd recommend?

Expected Behaviour

Location service starts and posts its foreground notification normally, consistent with the behavior we
see on all Android 11+ devices in our user base.

Additional Context

Beyond the code pattern flagged above, is there also a known interaction between the plugin's
foreground-service startup and OEM-level background process management (Oppo ColorOS / Vivo FuntouchOS)
specifically on Android 10 and below?

All data in this report is sourced directly from Firebase Crashlytics — stack trace, crash frequency, and
device/OS/app-version breakdown — pulled straight from live production crashes. Happy to pull anything
else Crashlytics exposes (breadcrumbs, custom keys, session timeline) if it would help narrow this down.

[Optional] Plugin Code and/or Config

const buildConfig = (token) => ({
desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
distanceFilter: 10,
disableElasticity: true,
elasticityMultiplier: 1,
motionTriggerDelay: 0,
disableLocationAuthorizationAlert: Platform.OS == "ios",
stopOnTerminate: false,
startOnBoot: true,
enableHeadless: true,
locationAuthorizationRequest: "Always",
stopTimeout: 5,
maxRecordsToPersist: -1,
maxDaysToPersist: 2,
autoSync: !!token,
autoSyncThreshold: 20,
batchSync: true,
maxBatchSize: 20,
url: ${BASE_URL}/user-location-tracking/ingest,
method: "POST",
httpRootProperty: ".",
headers: token ? { Authorization: Bearer ${token} } : {},
extras: { app_state: APP_STATE.FOREGROUND },
foregroundService: true,
notification: {
title: "Harbor Suite - Work Location",
text: "Location tracking is active for your work activities.",
text: "Location tracking is active for your work activities.",
channelName: "Location Tracking",
sticky: true,
},
locationUpdateInterval: 5000,
fastestLocationUpdateInterval: 2000,
deferTime: 0,
heartbeatInterval: 60,
preventSuspend: true,
activityType: BackgroundGeolocation.ActivityType.OtherNavigation,
pausesLocationUpdatesAutomatically: false,
showsBackgroundLocationIndicator: true,
useSignificantChangesOnly: false,
locationTimeout: 30,
debug: false,
logLevel: BackgroundGeolocation.LogLevel.Verbose,
});

[Optional] Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions