Files
LibreMail/app
JMR-devandClaude Fable 5 15c4cd9ae8 fix(security): harden app-lock recovery restart
The key-invalidation recovery restart was unreliable in two ways, both in
AppLockViewModel:

1. Same-process self-restart race: restartProcess() did
   context.startActivity(...) immediately followed by Runtime.exit(0) in the
   same process, so ActivityManager could schedule the relaunch into the
   process being killed and drop it — the app just closed, recovering only on
   the next manual launch. Fixed with a ProcessPhoenix-style separate-process
   trampoline (RestartActivity in a distinct ":restart" process, driven by
   ProcessRestarter): it kills the original process by PID and only then
   relaunches, so the relaunch is issued from a process that survives the kill.
   No new dependency; LibreMailApplication early-returns in the ":restart"
   process so it runs no normal startup work.

2. Lost syncNow() enqueue: clearCacheAndRestart() enqueued the post-wipe
   re-sync fire-and-forget, but WorkManager persists the WorkSpec
   asynchronously on its serial task executor, so exiting raced that insert and
   could drop the re-sync (now user-visible after #118: an empty mailbox until
   the next periodic sync). syncNow() now returns its enqueue Operation, and
   clearCacheAndRestart awaits it (bounded by a 5s timeout) before restarting,
   so the WorkSpec is durably persisted first.

CLEAR_PENDING recovery-flag semantics are preserved; the cache wipe still
happens at cold start in DatabaseModule (unchanged).

Tests: JVM unit tests assert the enqueue Operation is awaited before the
restart is triggered (order) and that a timed-out enqueue still restarts;
SyncSchedulerTest pins syncNow() returning the enqueue Operation. The
separate-process kill/relaunch is device-only and noted for on-device
wipe+resync verification.

Closes #99

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 10:09:21 -05:00
..