Full P0 Android documentation chain: - SysRS: API 24 → API 28 reconciliation per DEC-004 (SysRS-288); add SysRS-305 (Android in-call audio mode), SysRS-306 (RECORD_AUDIO runtime timing). - SysDes: SysDes-152 (in-call audio mode subsystem), SysDes-153 (RECORD_AUDIO permission flow), SysDes-154 (Android voice audio backend), SysDes-155 (macOS runtime baseline). - SRS: SRS-187 → API 28; add SRS-208 (in-call audio mode), SRS-209 (RECORD_AUDIO + listen-only fallback), SRS-210..215 (Android voice audio backend latency/preset/AEC/usage/sharing/foreground service); retarget SysDes anchors from generic SysDes-135 to SysDes-152/153/154. - SAD: SAD-063 refreshed (API 28); add SAD-084 (audio mode controller), SAD-085 (permission adapter with listen-only), SAD-086 (foreground service), SAD-087 (macOS runtime baseline); formalize cross-cutting + platform-specific allocation pattern in §24.1. - SDD: expand SDD-028 (BackIntentService); refresh SDD-073 (build config); add SDD-105 (JNI bootstrap), SDD-106 (permission requester), SDD-107 (foreground service), SDD-108 (audio mode controller), SDD-109 (AAB pipeline), SDD-110 (PTT capability), SDD-111..116 (Android voice audio backend), SDD-118 (Android bridge build automation), SDD-119 (iOS/macOS bridge build automation back-fill). - Verification: create android-p0-acceptance.md TC-1..TC-18; add SWE4-UV-040..052, SWE5-IV-016..026, SWE6-SV-018..030, SYS4-SIV-015 strengthened + SYS4-SIV-017/018. - Governance: traceability matrix v0.9.9 with end-to-end chain closure; DEC-032 documents the temporary abiFilters reduction to arm64-v8a only and its restore-by gate. Trace: full chain SysRS → SysDes → SRS → SAD → SDD → Code → Verification.
18 KiB
Android P0 acceptance — human-must test cases (v1.0.0-rc.8)
This document captures the human-side verification pass for the
Android P0 ship of Chanora. The officially-tested environment is
Android 9 (API 28) or later on a physical Android device, installed
via either Play Internal Track or bundletool install-apks (DEC-004).
Emulator support is best-effort — microphone capture and audio routing
in the AVD do not represent real-device behaviour.
Automated tests (cargo check, cargo test --workspace --lib,
flutter test, flutter build appbundle --release) sign off the
mechanical side. This checklist signs off the user-experience side.
A failure on any row blocks promotion of the Android build from rc.8.
Source: this checklist mirrors the iOS, iPadOS, macOS, Linux, and Windows acceptance documents. SRS references: SRS-013 (audio), SRS-111 (foreground service P0), SRS-119 (AAB packaging), SRS-163 (Android back navigation), SRS-187 (min API 28), SRS-188 (Play target API), SRS-208 (in-call audio mode), SRS-209 (RECORD_AUDIO + listen-only fallback). SAD references: SAD-018, SAD-037, SAD-063, SAD-077, SAD-081, SAD-084, SAD-085, SAD-086. SDD references: SDD-028, SDD-073, SDD-105, SDD-106, SDD-107, SDD-108, SDD-109, SDD-110. Decision: DEC-004 (Android min API 28). System requirements: SysRS-288, SysRS-305, SysRS-306.
Android does NOT have a global PTT capability comparable to Windows /
macOS / Linux desktops. Per SDD-110 the desktop PTT backend ladder
collapses to FocusedPttBackend on Android and the capability badge
always advertises L0Focused. PTT works only while Chanora itself is
the foreground app (and while the voice foreground service per
SDD-107 keeps the mic stream alive when the screen is off).
0. Pre-flight
| # | Step | Expected |
|---|---|---|
| 0.1 | On a Linux/macOS host install Android SDK + NDK + cmdline-tools. Accept SDK licences. | adb, bundletool, and Gradle wrapper are runnable. |
| 0.2 | Connect a physical Android device (API 28+) via USB. Enable Developer Options → USB debugging. | adb devices lists the device as device (not unauthorized). |
| 0.3 | Build the bridge for Android: rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android && cd ~/chanora && cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 -t x86 -p 28 -- build --release -p chanora_bridge. |
target/<abi>/release/libchanora_bridge.so exists for each ABI. (SDD-073 NDK ABI set, SDD-105 JNI load.) |
| 0.4 | Build the AAB: cd apps/chanora_flutter && flutter build appbundle --release. |
build/app/outputs/bundle/release/app-release.aab exists. (SRS-119, SDD-109) |
| 0.5 | Validate the AAB with bundletool: bundletool build-apks --bundle=app-release.aab --output=chanora.apks --connected-device. |
bundletool reports Universal APK/split APKs per ABI and no validation errors. (SDD-109) |
| 0.6 | Install on the connected device: bundletool install-apks --apks=chanora.apks. (Or Play Console → Internal Testing → upload + opt-in.) |
App installs without signature errors. (SDD-073 release-signing CI assertion.) |
| 0.7 | Launch the app. First-launch microphone prompt appears. | Prompt rationale is localized. (SRS-209, SDD-106) |
| 0.8 | About / version label inside Chanora | Shows v1.0.0-rc.8. |
Device matrix expectations
Per SRS-187 / SRS-188, DEC-004, AND SDD-116 (Android voice audio backend device matrix) the human pass shall be executed on the following devices:
| Tier | Android version | Reason | Acceptance subset |
|---|---|---|---|
| Floor | Android 9 (API 28) | Minimum supported per DEC-004 / SRS-187 / SysRS-288; SDD-116 row A | TC-1..TC-11 (all), TC-14 |
| Mid-tier | Android 12 (API 31) | Covers FGS foreground-type tightening (API 31); SDD-116 row B | TC-1..TC-11 (all), TC-12 predictive-back (if API ≥ 33), TC-15 |
| Target | Android 14 (API 34) per SRS-188 | Play upload + runtime target; SDD-116 row C; FGS-type=microphone strict on API 34 | TC-1..TC-11 (all), TC-13 Play Internal install path, TC-16 |
| OEM: Pixel | Any API ≥ 28 | SDD-116 OEM coverage — Pixel | TC-17 latency rig |
| OEM: Samsung | Any API ≥ 28 | SDD-116 OEM coverage — Samsung | TC-18 latency rig |
The Pixel and Samsung OEM rows MAY overlap with the API tier rows (e.g. the API 34 device may be a Pixel). The minimum aggregate is four physical devices when overlap is used, five otherwise.
TC-1: Install + identity persistence (DEC-006, SRS-201)
| # | Step | Expected |
|---|---|---|
| 1.1 | Enter a reachable TS3 server address, a nickname, blank password | Connect button enabled |
| 1.2 | Tap Connect | Server snapshot appears |
| 1.3 | Background + relaunch with the same nickname | Server-visible UID is the same (identity persisted in app-private files dir) |
Traces: SRS-013, SRS-209. SWE.6: SWE6-SV-018. SWE.5: SWE5-IV-016.
TC-2: Microphone permission state machine (SRS-209, SDD-106)
| # | Step | Expected |
|---|---|---|
| 2.1 | First run: tap PTT → system prompt appears → Allow. | Mic transmits. Capability badge says L0Focused. |
| 2.2 | Revoke RECORD_AUDIO via Settings → Apps → Chanora → Permissions while app is running. Return to Chanora and attempt to transmit. | App degrades to listen-only stream (per SRS-209 fallback). No crash. User-visible banner explains state. |
| 2.3 | Re-grant RECORD_AUDIO. | Transmit re-enables without restart (or via clearly-labelled "Retry" button). |
| 2.4 | Fresh install: deny the prompt twice (Android treats this as "permanently denied"). Attempt PTT. | App stays in listen-only; in-app banner deep-links to system settings. |
Traces: SRS-209, SysRS-306, SAD-085, SDD-106. SWE.4: SWE4-UV-041. SWE.5: SWE5-IV-017. SWE.6: SWE6-SV-019.
TC-3: Voice channel join + foreground service longevity (SRS-111, SDD-107)
| # | Step | Expected |
|---|---|---|
| 3.1 | Join a voice channel. | Foreground service notification appears with foregroundServiceType="microphone" (API 30+). Per SDD-107 the notification is non-dismissible while connected. |
| 3.2 | Lock the screen. Leave the device idle for ≥ 10 minutes. | Voice continues to be sent/received. No ANR, no service kill, no RemoteServiceException. |
| 3.3 | Open Settings → Battery while still connected. | Chanora is not listed as "restricted". (P0: warn user if it is.) |
| 3.4 | Disconnect voice channel. | Foreground service stops, notification disappears, AudioManager mode is restored (TC-6 verifies). |
Traces: SRS-111, SAD-081, SDD-107. SWE.4: SWE4-UV-043. SWE.5: SWE5-IV-018, SWE5-IV-019. SWE.6: SWE6-SV-018.
TC-4: PTT press / release (Focused only — Android limitation, SDD-110)
| # | Step | Expected |
|---|---|---|
| 4.1 | Open Voice settings → Push to talk | Capability badge clearly states L0 Focused with explanatory text noting Android does not expose global hotkeys to background apps. |
| 4.2 | Touch and hold the on-screen PTT button | Mic indicator goes active while held; releases on touch-up. |
| 4.3 | Background the app while holding PTT (swipe home). | Per SDD-110 PTT does NOT continue while backgrounded; transition is observable in the UI on return. |
Traces: SRS-195, SRS-196, SAD-086, SDD-110. SWE.4: SWE4-UV-044. SWE.6: SWE6-SV-024.
TC-5: Release tail
| # | Step | Expected |
|---|---|---|
| 5.1 | Set release-tail to 0 ms; tap-release | Sharp on/off. |
| 5.2 | Set release-tail to 500 ms; tap-release | Mic stays on ~500 ms after release. |
TC-6: AudioManager in-call mode round-trip (SRS-208, SDD-108)
| # | Step | Expected |
|---|---|---|
| 6.1 | Before connecting, capture AudioManager.getMode() via diagnostic export (or adb shell dumpsys audio). Note the value (typically MODE_NORMAL). |
Baseline recorded. |
| 6.2 | Connect + join voice channel. | Per SRS-208 / SDD-108 the controller enters in-call mode (MODE_IN_COMMUNICATION or platform equivalent). |
| 6.3 | Disconnect. | Per SDD-108 prior-mode snapshot is restored. Re-read AudioManager mode == baseline from 6.1. |
| 6.4 | Bluetooth SCO route check (P1 awareness, P0 minimum: no crash): pair a Bluetooth headset, connect Chanora voice. | P0: no crash, no permanent route lock. P1 follow-up: SCO routes correctly. |
Traces: SRS-208, SysRS-305, SAD-084, SDD-108. SWE.4: SWE4-UV-045. SWE.5: SWE5-IV-019. SWE.6: SWE6-SV-020. SYS.4: SYS4-SIV-017.
TC-7: Mic / speaker mute
| # | Step | Expected |
|---|---|---|
| 7.1 | Mute mic via VoiceBar | Mic icon shows muted state; PTT does not unmute. |
| 7.2 | Speaker mute | Other clients silenced locally. |
TC-8: Reconnect
| # | Step | Expected |
|---|---|---|
| 8.1 | Toggle airplane mode for 10 s then off | Connection-lost banner, then recovers. Foreground service does not crash during the transition. |
TC-9: Diagnostic export + privacy (DEC-027)
| # | Step | Expected |
|---|---|---|
| 9.1 | Export a diagnostic bundle (Settings → Diagnostics → Export). | Bundle is saved via the system Storage Access Framework picker. Allow-listed fields only; no raw key labels, no audio bytes, no RECORD_AUDIO permission token. |
Traces: SRS-117/SRS-118 family. SWE.6: SWE6-SV-006.
TC-10: Capability badge labels (SDD-110)
| # | Step | Expected |
|---|---|---|
| 10.1 | Capability badge | Shows L0Focused with text explaining Android does not expose session-level global hotkeys. |
TC-11: Android back navigation (SRS-163, SDD-028)
| # | Step | Expected |
|---|---|---|
| 11.1 | Open a modal sheet (e.g. Settings → Push-to-Talk binding sheet) and press the system back gesture / button. | Modal closes; route stack unchanged. (SDD-028 modal-first rule.) |
| 11.2 | From a leaf screen press back. | Pops one route. Does not exit the app unless on the home route. |
| 11.3 | While PTT is actively held, press back. | Back is ignored until PTT is released. (SDD-028 PTT-active-ignore rule.) |
| 11.4 | From the home route press back. | App backgrounds via standard activity finish (does not crash). |
Traces: SRS-163, SAD-018, SDD-028. SWE.4: SWE4-UV-040, SWE4-UV-042. SWE.5: SWE5-IV-020. SWE.6: SWE6-SV-021.
TC-12: Predictive back (Android 13+ API 33 branch, SDD-028)
| # | Step | Expected |
|---|---|---|
| 12.1 | On API 33+ enable "Predictive back animations" in Developer Options. Trigger a back gesture from a non-home route. | Predictive back preview shows; release commits the pop. SDD-028 API 33+ branch (OnBackInvokedCallback) is exercised. |
| 12.2 | On API ≤ 32 the same scenario uses the legacy OnBackPressedDispatcher branch (SDD-028 pre-33 branch). |
Same end behaviour; no predictive preview. |
Traces: SRS-163, SDD-028. SWE.4: SWE4-UV-042. SWE.6: SWE6-SV-021.
TC-13: Play Internal Track / bundletool install path (SRS-119, SRS-188, SDD-109)
| # | Step | Expected |
|---|---|---|
| 13.1 | Upload app-release.aab to Play Console → Internal Testing track (or via bundletool for the same effect). |
Play accepts the bundle: targetSdk matches SRS-188, minSdk == 28 per DEC-004, signing key is the release CI key per SDD-073. |
| 13.2 | Opt-in tester installs via Play store internal link. | App installs and runs identically to the bundletool path (TC-0.6). |
| 13.3 | Verify the AAB contains per-ABI split APKs (armeabi-v7a, arm64-v8a, x86_64). |
bundletool report lists all expected ABIs (SDD-109 ABI splits). |
Traces: SRS-119, SRS-188, SAD-063, SDD-073, SDD-109. SWE.5: SWE5-IV-020. SWE.6: SWE6-SV-022, SWE6-SV-023. SYS.4: SYS4-SIV-018.
TC-14: Per-device evidence collection on API 28 device (SDD-116 matrix row A)
| # | Step | Expected |
|---|---|---|
| 14.1 | On the API 28 device, complete pre-flight then voice_join a test channel. |
Channel join succeeds; FGS active per TC-3. |
| 14.2 | Export the backend diagnostics bundle (Settings → Diagnostics → Export, or the equivalent in-app action). | Export contains achieved_performance_mode, achieved_sharing_mode, achieved_input_preset, achieved_output_usage, achieved_output_content_type, and per-effect {aec, ns, agc} engagement (hw / sw / unavailable). |
| 14.3 | Record all six achieved values + per-effect engagement into the SDD-116 matrix table for the API 28 row. | All fields populated; no unknown. |
| 14.4 | voice_leave. |
Backend tears down cleanly per SWE5-IV-023 reverse order. |
Traces: SRS-210, SRS-211, SRS-212, SRS-213, SRS-214, SRS-215, SDD-111, SDD-112, SDD-113, SDD-116. SWE.6: SWE6-SV-026, SWE6-SV-027, SWE6-SV-028, SWE6-SV-029, SWE6-SV-030. SWE.5: SWE5-IV-021.
TC-15: Per-device evidence collection on API 31 device (SDD-116 matrix row B)
| # | Step | Expected |
|---|---|---|
| 15.1 | On the API 31 device, complete pre-flight then voice_join. |
Channel join succeeds. |
| 15.2 | Export diagnostics bundle. | Same fields as TC-14.2 populated. |
| 15.3 | Record values into SDD-116 matrix table for the API 31 row. | All fields populated. |
| 15.4 | Verify FGS-foreground-type tightening branch is exercised: dumpsys activity services <pkg> shows the voice service running. |
Service entry present; type recorded. |
| 15.5 | voice_leave. |
Clean teardown. |
Traces: SRS-210..SRS-215, SDD-111..SDD-116. SWE.6: SWE6-SV-026..030. SWE.5: SWE5-IV-021, SWE5-IV-023.
TC-16: Per-device evidence collection on API 34 device (SDD-116 matrix row C)
| # | Step | Expected |
|---|---|---|
| 16.1 | On the API 34 device, complete pre-flight then voice_join. |
Channel join succeeds. |
| 16.2 | Export diagnostics bundle. | Same fields as TC-14.2 populated. |
| 16.3 | Record values into SDD-116 matrix table for the API 34 row. | All fields populated. |
| 16.4 | Verify dumpsys activity services <pkg> shows foregroundServiceType=microphone for the voice service (API 34 type tightening). |
microphone type present. |
| 16.5 | Screen-off / locked for ≥ 10 minutes during a live voice session. | Mic frames continue (per-minute frame-count probe in diagnostics is non-zero each minute). No MissingForegroundServiceTypeException in logcat. |
| 16.6 | voice_leave. |
Clean teardown. |
Traces: SRS-210..SRS-215, SDD-107, SDD-111..SDD-116. SWE.6: SWE6-SV-026..030. SWE.5: SWE5-IV-021, SWE5-IV-023, SWE5-IV-026.
TC-17: Glass-to-glass latency measurement on Pixel device (SDD-116 OEM coverage)
| # | Step | Expected |
|---|---|---|
| 17.1 | Set up the latency rig per swe6-software-verification-plan.md "Glass-to-glass latency measurement procedure" (DUT = Pixel device, reference listener on LAN, stereo capture on audio interface). |
Rig produces clean impulse / listener channels. |
| 17.2 | Record achieved_performance_mode for the Pixel DUT. |
Recorded (typically LowLatency on Pixel). |
| 17.3 | Run 20 impulses; compute median and p95 latency. | Both recorded. |
| 17.4 | Compare against SRS-210 tier per SWE6-SV-025 pass criteria. | If LowLatency: median ≤ 150 ms, p95 ≤ 180 ms. Otherwise: median ≤ 250 ms, p95 ≤ 300 ms. |
| 17.5 | Archive raw stereo WAVs + measurement script + per-impulse CSV. | Artifacts attached to test report. |
Traces: SRS-210, SDD-111, SDD-112, SDD-116. SWE.6: SWE6-SV-025. SWE.4: SWE4-UV-051 (tier classifier).
TC-18: Glass-to-glass latency measurement on Samsung device (SDD-116 OEM coverage)
| # | Step | Expected |
|---|---|---|
| 18.1 | Re-run the TC-17 rig with the DUT swapped to a Samsung device. | Rig setup unchanged. |
| 18.2 | Record achieved_performance_mode for the Samsung DUT. |
Recorded. |
| 18.3 | Run 20 impulses; compute median and p95 latency. | Both recorded. |
| 18.4 | Compare against SRS-210 tier per SWE6-SV-025 pass criteria. | Same thresholds as TC-17.4 against the Samsung-achieved tier. |
| 18.5 | Archive evidence as TC-17.5. | Artifacts attached. |
Traces: SRS-210, SDD-111, SDD-112, SDD-116. SWE.6: SWE6-SV-025. SWE.4: SWE4-UV-051.
Auto-test sign-off
These rows do not require human interaction.
| Layer | Result |
|---|---|
cargo check --workspace |
clean |
cargo test --workspace --lib |
unchanged (Android-specific Rust code is #[cfg(target_os = "android")]-gated) |
cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 -p 28 -- build --release -p chanora_bridge |
clean per ABI |
flutter test in apps/chanora_flutter |
green; widget + e2e suites carry SRS trace headers |
flutter build appbundle --release |
clean — produces app-release.aab |
bundletool validate --bundle=app-release.aab |
no errors |
Gradle assertMinSdk28 task (per SDD-073) |
passes; minSdk in merged manifest == 28 |
| R8 / ProGuard release pass (per SDD-073) | clean; mapping file archived |
| CI release-signing key custody check (per SDD-073) | signing key reference matches expected fingerprint |
Sign-off form
chanora v1.0.0-rc.8 — Android P0 acceptance
Device 1 (API 28): ______________________ (model + Android version)
Device 2 (mid): ______________________ (model + Android version)
Device 3 (target): ______________________ (model + Android version)
Tester: ______________________
Date: ______________________
TC-1 Install + identity persistence [ pass / fail ] notes:
TC-2 RECORD_AUDIO state machine [ pass / fail ] notes:
TC-3 Voice channel + FGS longevity [ pass / fail ] notes:
TC-4 PTT press / release (Focused) [ pass / fail ] notes:
TC-5 Release tail [ pass / fail ] notes:
TC-6 AudioManager mode round-trip [ pass / fail ] notes:
TC-7 Mic / speaker mute [ pass / fail ] notes:
TC-8 Reconnect [ pass / fail ] notes:
TC-9 Diagnostic export + privacy [ pass / fail ] notes:
TC-10 Capability badge label [ pass / fail ] notes:
TC-11 Back navigation [ pass / fail ] notes:
TC-12 Predictive back (API 33+) [ pass / fail ] notes:
TC-13 Play Internal / bundletool install [ pass / fail ] notes:
TC-14 Per-device evidence API 28 (SDD-116 A)[ pass / fail ] notes:
TC-15 Per-device evidence API 31 (SDD-116 B)[ pass / fail ] notes:
TC-16 Per-device evidence API 34 (SDD-116 C)[ pass / fail ] notes:
TC-17 Latency rig — Pixel [ pass / fail ] notes:
TC-18 Latency rig — Samsung [ pass / fail ] notes:
Overall result: [ Android rc.8 PASS / Android rc.8 FAIL ]
Sign: ______________________
Known gaps (not blockers)
- Bluetooth SCO routing: P0 only requires no-crash behaviour. Full SCO route correctness is P1.
- Background PTT: not possible on Android per platform model;
FocusedPttBackendonly (SDD-110). - VAD (
TransmitMode::VoiceActivity) intentionally disabled in P0 (DEC-030). - Tablet / foldable layouts: this checklist targets phone form factor. The wide-mode (≥ 840 dp) UI works but is not signed off here.
- Play Console upload key custody: depends on whoever holds the release keystore; CI signing assertion (SDD-073) covers the mechanical side only.