Files
chanora/poc
EdisonJwa ec21a880d2 feat(poc/audio): add Android mobile audio spike
Closes the mobile half of the PoC plan §2 audio exit criterion
left open by poc/audio-capture-playback-spike. The desktop and
mobile halves together fully retire the audio PoC.

Stack:
  Kotlin (MainActivity) → JNI → Rust cdylib
    → cpal 0.16 → Oboe (AAudio / OpenSL ES) → Android audio HAL

Layout:
  rust crate (src/lib.rs)   — JNI_OnLoad, initContext,
                              playSine440, record1sToFile;
                              panic-catching at JNI boundary;
                              android_logger → logcat
  android/ (Gradle 8.7,     — minSdk 24, compileSdk 34, AGP 8.5.2.
   AGP 8.5.2, Kotlin 1.9.24)  cargoBuildRust task wraps cargo-ndk
                              -P 26 -t <abi> for all four ABIs;
                              wired into preBuild so AGP picks up
                              the produced .so files.

Verified on 2026-05-13 on a physical Motorola Moto G Stylus 5G
(2023), Android 14 SDK 34 arm64-v8a:
  - Playback: 500 ms 440 Hz mono sine, 22,050 frames emitted at
    44.1 kHz through cpal/Oboe/AAudio/device speaker.
  - Capture: 1 s from default input, 42,624 frames written to
    /data/data/app.chanora.poc.audio/files/chanora_poc_capture.wav.
    File pulled via 'adb exec-out run-as ... cat' and confirmed
    by file(1) as 'RIFF (little-endian) data, WAVE audio,
    Microsoft PCM, 16 bit, mono 44100 Hz'. Header bytes
    cross-checked against the reported frame count.

Notes:
  - cpal links libaaudio (introduced API 26), so cargo-ndk targets
    API 26 via -P 26 while the Android module's minSdk stays at 24
    (DEC-004). API 24/25 devices would fall back to OpenSL ES at
    runtime; not exercised here.
  - JNI panic safety: every JNI entry point wraps its body in
    std::panic::catch_unwind and a tracing panic hook routes
    panic messages to logcat under tag 'ChanoraAudioPoC'. Without
    this, cpal panicking inside an extern "system" function would
    abort the process.
  - The emulator AVD chanora-poc-api34 and its system image were
    installed during Phase 0 but emulator verification was skipped
    once the physical-device run succeeded. Real-device evidence
    is stronger.

Surfaced finding: DEC-011.1 mobile half promoted from Deferred to
Accepted for Android in the same docs commit; iOS remains
explicitly Deferred (requires macOS + Xcode hardware).

Authority: PoC plan §2, DEC-011, DEC-011.1.
Not product code; not promoted into chanora_audio.
2026-05-14 18:24:53 +08:00
..

Chanora — Proofs of Concept

This directory holds non-product technical spikes. They exist to retire risk before the Flutter/Rust product scaffold is created.

Authority: docs/architecture/proof-of-concept-plan.md.

Rule (PoC plan §4): A PoC is not product code unless explicitly promoted. Nothing here should be imported by the future apps/chanora_flutter or crates/chanora_* trees.

Status

Spike Required by PoC plan Status
tsclientlib-connect-spike yes PASS — see tsclientlib-connect-spike/VERIFICATION.md
flutter_rust_bridge_hello yes (flutter-rust-bridge-hello in plan) PASS — see flutter_rust_bridge_hello/VERIFICATION.md
secure-storage-spike yes PASS (Linux only) — see secure-storage-spike/VERIFICATION.md
audio-capture-playback-spike yes PARTIAL PASS — desktop verified, mobile not — see audio-capture-playback-spike/VERIFICATION.md
sqlite-storage-spike yes PASS — see sqlite-storage-spike/VERIFICATION.md
diagnostics-redaction-spike yes PASS — see diagnostics-redaction-spike/VERIFICATION.md

Naming note

flutter_rust_bridge_hello is listed in the PoC plan as flutter-rust-bridge-hello. The directory on disk uses underscores because the Dart pubspec.yaml package-name grammar forbids hyphens. This is a naming convention, not a scope change.

What "verified" means here

Each verified spike contains:

  • a README.md describing scope, exit criterion, and reproduction;
  • a VERIFICATION.md recording the exact toolchain versions, command, observed output, and an honest list of things the spike does not validate.

Spikes are intentionally narrow. They do not approximate Chanora architecture layering, they do not implement the documented DTO catalogue, and they do not stand in for the product Rust crates or Flutter modules.