microg/GmsCore · error · RuntimeException
Binder died
Error message
Binder died
What it means
Before delivering a location update, LocationRequestManager.check() verifies that the client's callback Binder (ILocationListener) is still alive. If the client process died or unbound without removing its listener, callback.asBinder().isBinderAlive is false and a RuntimeException 'Binder died' is thrown instead of delivering to a dead endpoint.
Source
Thrown at play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt:459
fun start(): LocationRequestHolder {
if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException("Lack of permission")
return this
}
fun cancel() {
try {
callback?.cancel()
} catch (e: Exception) {
Log.w(TAG, e)
}
}
fun check() {
if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException("Lack of permission")
if (effectiveGranularity > permissionGranularity) throw RuntimeException("Lack of permission")
if (timePendingMillis < 0) throw RuntimeException("duration limit reached (active for ${(SystemClock.elapsedRealtime() - start).formatDuration()}, duration ${request.durationMillis.formatDuration()})")
if (updatesPending <= 0) throw RuntimeException("max updates reached")
if (callback?.asBinder()?.isBinderAlive == false) throw RuntimeException("Binder died")
}
fun processNewLocation(location: Location): Boolean {
check()
if (lastLocation != null && location.elapsedMillis - lastLocation!!.elapsedMillis < request.minUpdateIntervalMillis) return false
if (lastLocation != null && location.distanceTo(lastLocation!!) < request.minUpdateDistanceMeters) return false
if (lastLocation == location) return false
val returnedLocation = if (effectiveGranularity > permissionGranularity) {
throw RuntimeException("Lack of permission")
} else {
if (!context.noteAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) {
throw RuntimeException("app op denied")
} else if (clientIdentity.isSelfProcess()) {
Location(location)
} else {
Location(location).apply { provider = "fused" }
}
}View on GitHub (pinned to 157c9d86ac)
Solutions
- Always call FusedLocationProviderClient.removeLocationUpdates() in onDestroy/onStop or when the client is torn down
- Re-register the location request after process restart instead of relying on the old registration
- Ensure the client process is alive (foreground service / proper lifecycle) for the duration of the request
- If you control the manager side, treat isBinderAlive==false as a signal to unregister the request rather than propagate the exception
Example fix
// before
override fun onStart() {
client.requestLocationUpdates(request, listener, looper)
}
// after
override fun onStart() {
client.requestLocationUpdates(request, listener, looper)
}
override fun onStop() {
client.removeLocationUpdates(listener) // avoids orphaned request on process death
} Defensive patterns
Strategy: try-catch
Try / catch
try {
client.requestLocationUpdates(request, listener, looper)
} catch (e: RuntimeException) {
if (e.message == "Binder died") {
// re-register on the new process lifecycle
registerOnNextStart()
} else throw e
} Prevention
- Always pair requestLocationUpdates with removeLocationUpdates in lifecycle teardown
- Use a foreground service for long-running location needs so the process is not killed
- Handle onServiceDisconnected / process death and re-register the listener
- Avoid keeping listeners registered while backgrounded if the OS may kill the app
When it happens
Trigger: The client app process was killed (or crashed) while a location request was still registered; the request was not removed via removeLocationUpdates before the process exited; the next location arrival triggers processNewLocation() -> check().
Common situations: Backgrounded apps killed by the OS while holding an active location request; crashes during testing leaving orphaned requests; a client that unbinds its service without cleaning up listeners; debugging with 'Don't keep activities' enabled.
Related errors
- suggested UID [
- suggested PID [
- No fields were found
- Too many non-synthetic fields were found
- Binder object is null.
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/782b57cd30931a50.
Report an issue: GitHub.