Context
ReplayIntegration uses lifecycleLock to synchronize lifecycle state transitions (start, stop, pause, close, checkCanRecord). This has led to recurring deadlocks when the lock and the single-threaded replay executor wait on each other:
With plans to expose start/stop/resume/pause as public API for manual control, users could call from any thread, making lock-ordering issues harder to reason about over time.
Proposal
Confine all lifecycle state mutations to a single thread (main looper or a dedicated HandlerThread). Public methods post to that handler and return — callers don't need to know the execution context. The replay executor stays for heavy work (encoding, caching) but never touches lifecycle state.
This eliminates lifecycleLock entirely. No lock → no lock ordering → no deadlock class of bugs.
Android's own lifecycle works this way: finish() can be called from any thread, but the state transition happens on main.
Open question
Whether any caller needs synchronous confirmation that the operation completed. If stop() must guarantee "replay is stopped when I return," confinement needs post + await. For replay lifecycle ops this likely isn't needed.
References
Context
ReplayIntegrationuseslifecycleLockto synchronize lifecycle state transitions (start,stop,pause,close,checkCanRecord). This has led to recurring deadlocks when the lock and the single-threaded replay executor wait on each other:checkCanRecordrunning on executor, blocking onlifecycleLockclose()holding lock duringawaitTerminationWith plans to expose
start/stop/resume/pauseas public API for manual control, users could call from any thread, making lock-ordering issues harder to reason about over time.Proposal
Confine all lifecycle state mutations to a single thread (main looper or a dedicated
HandlerThread). Public methods post to that handler and return — callers don't need to know the execution context. The replay executor stays for heavy work (encoding, caching) but never touches lifecycle state.This eliminates
lifecycleLockentirely. No lock → no lock ordering → no deadlock class of bugs.Android's own lifecycle works this way:
finish()can be called from any thread, but the state transition happens on main.Open question
Whether any caller needs synchronous confirmation that the operation completed. If
stop()must guarantee "replay is stopped when I return," confinement needspost + await. For replay lifecycle ops this likely isn't needed.References