77> RetroAchievements, and Lua** to a follow-up point release. A shared ** ` rustynes-mobile ` **
88> bridge crate is factored here and reused by the v1.9.0 iOS plan.
99
10+ > ** Monetization LOCKED by the maintainer (2026-06-19):** ship on the ** Google Play Store**
11+ > as a ** free download with a one-time in-app "Full Unlock" purchase priced at $2.99 USD**
12+ > (a managed/non-consumable IAP via Play Billing — the modern single-listing freemium model,
13+ > NOT two separate paid/free listings). The ** free tier is a time-limited demo** : a ** 10-minute
14+ > play session per launch** , and with ** save-states, save-on-background/resume, and on-cart
15+ > battery-backed SRAM persistence all disabled** . Everything else (full emulation accuracy,
16+ > video, audio, input, pause/fast-forward, the whole ROM library) is ** identical to the paid
17+ > tier** — the demo is feature-complete except the three persistence gates + the session timer.
18+ > Purchasing the Full Unlock removes the timer and enables save-states / resume / SRAM. See the
19+ > "Monetization / licensing (Workstream M)" section below.
20+
1021Research backing: [ ` research/v1.8.0-research-android.md ` ] ( research/v1.8.0-research-android.md ) .
1122
1223## Context
@@ -161,6 +172,41 @@ language + the 2026–27 developer-verification rollout by (1) preparing dev ver
161172depends solely on Play (also the home for the egui-debugger power-user build). ** Effort: M;
162173approval/calendar risk is outside our control.**
163174
175+ ### M — Monetization / licensing (freemium $2.99 unlock) → rc.1
176+ A ** single free Play listing** with a ** one-time, non-consumable in-app purchase** ("Full
177+ Unlock", product id ` full_unlock ` , ** $2.99 USD** ) via the ** Play Billing Library** (`com.android.
178+ billingclient: billing-ktx `, v7+). NOT a paid app and NOT a second "demo" listing — Play's modern
179+ guidance is one free app + an unlock IAP. Pieces:
180+
181+ - ** ` Entitlement ` / ` BillingManager ` ** (new Kotlin): on start, connect ` BillingClient ` , query the
182+ ` full_unlock ` product + the user's purchases, and expose a ` @Volatile isUnlocked ` the shell
183+ reads. A purchase must be ** acknowledged** (` acknowledgePurchase ` ) or Play auto-refunds in 3
184+ days. Entitlement is cached locally (DataStore) for offline launches but is ** re-verified
185+ against Play** on each connect (the cache is a convenience, not the source of truth). No
186+ server: a non-consumable IAP is owned forever and restored via ` queryPurchasesAsync ` (handles
187+ reinstall / new device / "restore purchases").
188+ - ** Demo gating (the three locks + the timer), all enforced in the shell, core untouched:**
189+ 1 . ** 10-minute session timer** — a monotonic per-launch budget; on expiry, pause the emulator
190+ and show a non-dismissable "Demo time's up — Unlock full version for $2.99" sheet with the
191+ Buy + Restore buttons. Resets on next launch (per-session, not per-day).
192+ 2 . ** Save-states disabled** — the Save/Load slot buttons are hidden/disabled in demo.
193+ 3 . ** Save-on-background + auto-resume disabled** — ` onPause ` writes no ` auto ` state and a ROM
194+ never auto-resumes in demo.
195+ 4 . ** Battery-backed SRAM not persisted** — the on-cart save RAM is never written to disk in
196+ demo (a battery save inside the snapshot is fine in-session, but nothing survives close).
197+ When ` isUnlocked ` flips true (purchase or restore), all four lift immediately, no restart.
198+ - ** UI:** an always-visible "Unlock ($2.99)" affordance in demo; a "Restore purchase" entry; a
199+ subtle "Demo — N: MM left" indicator. After unlock, these disappear.
200+ - ** Determinism-safe / accuracy-safe:** every gate is a * host* policy over persistence + pacing;
201+ the emulated framebuffer/audio for a given input is ** byte-identical** in demo and paid (the
202+ demo is the same emulator, just without the persistence features + a session clock). The core,
203+ the ` .rns ` format, and AccuracyCoin are untouched.
204+ - ** Maintainer-manual (can't be CI-self-certified):** create the ` full_unlock ` managed product
205+ in the Play Console, the pricing ($2.99) + tax, a license-tested account, and the closed-track
206+ upload to exercise the real purchase flow (Play Billing can't be fully tested on a sideloaded
207+ build). A ** ` BuildConfig ` /debug override** (` debug.force_unlocked ` / ` debug.force_demo ` ) lets the
208+ gating + timer + UI be verified on-device without Play. ** Effort: M.**
209+
164210### Perf / thermal / battery * (cross-cutting, in B/C/F)*
165211Perf is ** not** the gate (the NES is light: ≤2 ms/frame headless, ample on modern arm64) —
166212** thermals/battery** are. Default run-ahead ** off or 1** on Android (keep the budget-throttle
@@ -177,7 +223,7 @@ run-ahead/fast-forward. **Determinism-safe** — all frontend pacing/scheduling
177223| ** beta.3 — Audio + input + touch** | C + D | (c) | audio lifecycle/latency; input determinism (touch + gamepad → one mask) |
178224| ** beta.4 — Storage + SAF + states** | E | (c) | ** Risk #3 ** no-bundled-ROM/SAF + persistable grants; ` .rns ` cross-device |
179225| ** beta.5 — Compose shell + feature port** | F | (c) | native feel + parity (shaders/TAS/palettes); RA/netplay/Lua marked deferred |
180- | ** rc.1 — Store/legal/signing** | G | (c) | ** Risk #2 ** Play policy (partial) + signing + 16 KB align + AAB in CI |
226+ | ** rc.1 — Store/legal/signing + monetization ** | G + M | (c) | ** Risk #2 ** Play policy (partial) + signing + 16 KB align + AAB in CI; ** freemium $2.99 unlock + 10-min demo gating ** |
181227| ** v1.8.0 release** | docs (` docs/android.md ` ), CHANGELOG, ` rustynes-mobile ` +` rustynes-android ` +Gradle committed, signed AAB on a closed track + sideload APK | (c) | full host-CI gate suite green; AccuracyCoin 100%/139 |
182228
183229## Critical files / crates
0 commit comments