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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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