fix(upgrade): Align the round-4 restore, recorder sentinel and contention test

Restore the consumed sponsor marker only when no newer launch was armed
meanwhile. Make the recorder's monotonic base a nullable sentinel so a
boot-adjacent elapsedRealtime of 0 is not mistaken for a resumed session,
and clear it on stop. Pin the contention test's collector subscription and
assert it actually echoed.
This commit is contained in:
darken
2026-08-03 20:28:01 +02:00
committed by Matthias Urhahn
parent 9b60ed945f
commit 495f58c23b
5 changed files with 118 additions and 9 deletions
@@ -173,10 +173,16 @@ class UpgradeViewModel @Inject constructor(
} catch (e: Exception) {
// The marker was consumed above; neither a failed entitlement read nor a failed write may
// eat the user's valid sponsor visit — restore it so the next return/resume can retry the
// unlock. Rethrow unconditionally: cancellation is not swallowed, other errors surface via
// the normal error path. A restored marker after a successful persist is harmless — the
// next evaluation hits the quiet isPro path.
handle[KEY_SPONSOR_PRESSED_AT] = pressedAt
// unlock. Conditional: the user may have armed a NEWER launch while this attempt was
// suspended, and that one must survive. The contains-check has a small check-then-act
// window against a concurrent new arm; accepted — the create-only transaction owns data
// integrity, a wrong winner only changes which REAL visit's timestamp gates the unlock.
// Rethrow unconditionally: cancellation is not swallowed, other errors surface via the
// normal error path. A restored marker after a successful persist is harmless — the next
// evaluation hits the quiet isPro path.
if (!handle.contains(KEY_SPONSOR_PRESSED_AT)) {
handle[KEY_SPONSOR_PRESSED_AT] = pressedAt
}
throw e
}
}