Skip to content

[Bug] system_server soft reboot: IllegalStateException in LocationEventLog$AggregateStats — identical trace to #26, still present in v0.1.3 #50

Description

@godadom

Summary

system_server dies with an unhandled IllegalStateException in
LocationEventLog$AggregateStats.markRequestBackground, causing a soft reboot
(framework restart — the kernel stays up). The stack trace is byte-identical
to the one in #26 apart from vendor line-number offsets, but on completely
different hardware and a different OEM framework, and it still occurs on
v0.1.3, the current release.

#26 was closed without a stated fix and its reporter was on v0.0.8/0.0.9. Filing
separately to establish that the bug is (a) not device- or OEM-specific and
(b) still present in the latest version.

Related: #49 (a separate, reproducible hook bug in the same subsystem on this
device).

Environment

Device Redmi K90 Pro Max (25102RKBEC, codename myron)
Build Redmi/myron/myron:16/BP2A.250605.031.A3/OS3.0.306.0.WPMCNXM:user/release-keys
Android 16 (SDK 36), HyperOS 3
XposedFakeLocation v0.1.3 (versionCode 103)
Root KernelSU ksud 4.1.3
Xposed LSPosed v2.1.1 (7790), ZygiskNext 1.4.3
Module scope System Framework + several apps

The crash

Happened once, ~14 hours into uptime, with the device lightly used but under
moderate load (load average 11.85). Not reproducible on demand.

FATAL EXCEPTION IN SYSTEM PROCESS: android.fg
java.lang.IllegalStateException
	at com.android.internal.util.Preconditions.checkState(Preconditions.java:222)
	at com.android.internal.util.Preconditions.checkState(Preconditions.java:207)
	at com.android.server.location.eventlog.LocationEventLog$AggregateStats.markRequestBackground(LocationEventLog.java:728)
	at com.android.server.location.eventlog.LocationEventLog.logProviderClientBackground(LocationEventLog.java:202)
	at com.android.server.location.provider.LocationProviderManager$Registration.onForegroundChanged(LocationProviderManager.java:776)
	at com.android.server.location.provider.LocationProviderManager.lambda$onAppForegroundChanged$14(LocationProviderManager.java:2749)
	at com.android.server.location.provider.LocationProviderManager$$ExternalSyntheticLambda26.test(D8$$SyntheticClass:0)
	at com.android.server.location.listeners.ListenerMultiplexer.updateRegistrations(ListenerMultiplexer.java:525)
	at com.android.server.location.provider.LocationProviderManager.onAppForegroundChanged(LocationProviderManager.java:2749)
	at com.android.server.location.provider.LocationProviderManager$$ExternalSyntheticLambda11.onAppForegroundChanged(D8$$SyntheticClass:0)
	at com.android.server.location.injector.AppForegroundHelper.notifyAppForeground(AppForegroundHelper.java:71)
	at com.android.server.location.injector.SystemAppForegroundHelper.lambda$onAppForegroundChanged$0(SystemAppForegroundHelper.java:59)
	at com.android.server.location.injector.SystemAppForegroundHelper$$ExternalSyntheticLambda1.run(D8$$SyntheticClass:0)
	at android.os.Handler.handleCallback(Handler.java:1029)
	at android.os.Handler.dispatchMessage(Handler.java:107)
	at android.os.Looper.loopOnce(Looper.java:274)
	at android.os.Looper.loop(Looper.java:369)
	at android.os.HandlerThread.run(HandlerThread.java:85)
	at com.android.server.ServiceThread.run(ServiceThread.java:49)

Confirmed as a framework restart, not a kernel reboot: DropBox recorded
SYSTEM_RESTART (not SYSTEM_BOOT), sys.runtime.reboot.reason.last was
je#system_server#<ts>#unhandled, and /proc/uptime showed the kernel up
continuously for 13h51m across the event. No kernel panic, no pstore record, no
native tombstone.

Ruled out: thermal (status NONE, 36 °C battery, 54–60 °C CPU), battery/power
loss, storage, and native crashes.

Identical to #26 across two unrelated devices

Frame #26 (Meizu 21 Pro) Here (Redmi K90 Pro Max)
AggregateStats.markRequestBackground LocationEventLog.java:728 LocationEventLog.java:728
logProviderClientBackground LocationEventLog.java:202 LocationEventLog.java:202
Registration.onForegroundChanged LocationProviderManager.java:668 LocationProviderManager.java:776
ListenerMultiplexer.updateRegistrations :525 :525
AppForegroundHelper.notifyAppForeground AppForegroundHelper.java:71 AppForegroundHelper.java:71
SystemAppForegroundHelper.lambda$...$0 SystemAppForegroundHelper.java:59 SystemAppForegroundHelper.java:59

Only LocationProviderManager differs, as expected between vendor framework
builds. Two OEMs, two Android 16 images, same assert, same module.

The #26 reporter also noted the crash was not specific to Douyin and happened
with "all common app I used with location function." That matches here — no
single target app was in use at the moment of my crash.

Proposed mechanism

AggregateStats guards a foreground/background state machine with
Preconditions.checkState, so markRequestBackground throws if the counter is
already in the background state. Counters are keyed by
provider → uid/package[attributionTag] — visible in dumpsys location under
Historical Aggregate Location Provider Data — so a double-mark needs either two
registrations sharing one counter, or a registration whose foreground marker went
stale.

The module produces churn that can strand that marker. From dumpsys location,
it removes and re-adds its registrations within 1 ms, repeatedly:

15:44:57.649  network -registration 10388/com.noobexon.xposedfakelocation/0EE68DF0
15:44:57.649  gps     -registration 10388/com.noobexon.xposedfakelocation/0EE68DF0
15:44:57.650  network +registration 10388/com.noobexon.xposedfakelocation/0EE68DF0   ← 1 ms later
15:44:57.652  gps     +registration 10388/com.noobexon.xposedfakelocation/0EE68DF0

Over a 10-minute sample the module was the second-highest source of location
registration events on the device
(14), behind only android itself. If an
app-foreground transition lands inside one of those remove/re-add windows, the
registration is torn down while still marked foreground and rebuilt fresh — so
the next background transition marks background on a counter that already
believes it is in the background. That is exactly the assert that fires.

What I have not proven

Being straightforward about the limits:

  • No module frames appear in the fatal stack. It is entirely platform code.
  • I could not reproduce on demand; it happened once in ~2 days of uptime.
  • I could not capture the crash-time dumpsys location, so I cannot name the
    package whose counter desynced.
  • An A/B test (module scoped to System Framework vs not) was inconclusive,
    because the precursor state never occurred in either 10-minute window.

What makes this more than a guess is the cross-device stack match with #26 under
the same module, on unrelated hardware.

Hooks installed into system_server

25 methods on this build, for reference:

LocationProviderManager#onReportLocation
LocationManagerService#{getLastLocation, getCurrentLocation, requestGeofence,
  addGnssMeasurementsListener, addGnssNavigationMessageListener,
  addGnssAntennaInfoListener, registerGnssStatusCallback, registerGnssNmeaCallback}
GnssManagerService#{addGnssMeasurementsListener, addGnssNavigationMessageListener,
  addGnssAntennaInfoListener, registerGnssStatusCallback, registerGnssNmeaCallback}
MiuiBlurLocationManagerImpl#{getBlurryLocation, getBlurryCellLocation,
  getBlurryCellInfos, handleGpsLocationChangedLocked}
SystemServiceManager#loadClassFromLoader          (see #49)
WifiServiceImpl#{getConnectionInfo, getScanResults}
PhoneInterfaceManager#{getAllCellInfo, getCellLocation, getNeighboringCellInfo,
  requestCellInfoUpdateInternal}

Workaround

Un-scoping the module from System Framework in LSPosed (keeping it scoped to
individual target apps) removes all 25 hooks and eliminates the registration
churn from system_server; afterwards the module no longer appears anywhere in
dumpsys location. This disables system-wide spoofing, so it is a mitigation,
not a fix.

Suggested fixes

  1. Avoid the remove/re-add cycle when updating the module's own location
    requests — update the existing registration in place, so no window exists in
    which a foreground transition can strand the AggregateStats marker.
  2. Consider whether the module needs its own real LocationManager registrations
    inside system_server at all, given it already intercepts the delivery path
    via LocationProviderManager#onReportLocation.
  3. If registrations must be recreated, doing so off the android.fg handler, or
    coalescing them, would narrow the race considerably.

I still have full logcat, LSPosed verbose/module logs, and dumpsys location
captures from before and after the crash, and am happy to run further diagnostics
or test a patch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions