`backgroundInactivityTimeout` miscounts on Android due to a clock that pauses in deep sleep
1) backgroundInactivityTimeout miscounts on Android due to a clock that pauses in deep sleep
Component(s): android-agent, android, SessionManager, SessionConfig
Environment: Android (device enters Doze / deep sleep)
Summary
backgroundInactivityTimeout in SessionConfig behaves incorrectly on Android when devices enter deep sleep. The default Clock uses System.nanoTime() which maps to a monotonic source that does not advance during deep sleep. Background duration is undercounted, so sessions may not rotate when they should.
Expected behavior
Background inactivity should be measured against a time base that continues through device sleep so a configured timeout triggers after the intended real elapsed duration.
Actual behavior
When the app is backgrounded and the device sleeps, System.nanoTime() stops. On resume, SessionIdTimeoutHandler/SessionManager see a shorter elapsed interval than real time, preventing or delaying session rotation.
Why this happens (Android specifics)
System.nanoTime()≈CLOCK_MONOTONIC→ pauses during deep sleep.SystemClock.elapsedRealtime()/elapsedRealtimeNanos()→ includes deep sleep; correct for elapsed-wall semantics.
Minimal reproduction
- Set
backgroundInactivityTimeoutto a small value (e.g., 5 min). - Background the app; allow the device to enter deep sleep > timeout.
- Resume the app.
- Observe that the session did not rotate as expected.
Proposed change
- Provide an Android-aware
Clockthat usesSystemClock.elapsedRealtimeNanos()for elapsed measurements. - Pass this
ClocktoSessionManagerandSessionIdTimeoutHandlerduring init. - Remove the secondary
SessionManagerconstructor (marked “for tests”) and add a factory method to ensure consistent wiring in prod/tests.
related to https://github.com/open-telemetry/opentelemetry-android/issues/1364
@breedx-splk assign this to me
@breedx-splk @atulgpt @fractalwrench plan to implement the fix for an android aware Clock implementation using SystemClock.elapsedRealtimeNanos() that includes deep sleep time. Replace the default Clock.getDefault() which uses System.nanoTime() in SessionIdTimeoutHandler and SessionManager. Update the secondary constructor in SessionIdTimeoutHandler to use a factory method, and wire the new Clock through production initialization in OpenTelemetryRumInitializer.
@namanONcode @atulgpt has offered to implement a fix already in #1364. I'll close out this issue as on reflection it'd be better to track all discussion in the other issue.