Files
chanora/docs/verification/android-p0-acceptance.md
T
EdisonJwa 5aa51c310f docs(p0): SysRS/SysDes/SRS/SAD/SDD/Verification + traceability for Android P0 reconciliation
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.
2026-05-18 12:48:28 +08:00

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; FocusedPttBackend only (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.