# Exploration: alarm-volume-ramp-restore ## Current State — dual-track handoff architecture (by design) Alarm audio is a deliberate handoff (documented in `app-quality-and-native-alarms/design.md` Decision 1.5, `docs/alarmas-pantalla-apagada.md`): 1. **Native track**: `PluriWaveAlarmReceiver(ACTION_FIRE)` → `PluriWaveAlarmService.kt` plays via `MediaPlayer` with `AudioAttributes.USAGE_ALARM` (L350-354) — Android ALARM stream, immune to media-volume-0 by OS design. Has a working fade-in (`startFadeIn`, `FADE_IN_START_FRACTION = 0.05f`, 250ms steps). 2. **Flutter track**: `app.dart` `_prearrancarAudioAlarma()` starts the regular radio player (`PluriWaveAudioHandler`/just_audio) at volume 0.05; `pantalla_alarma_sonando.dart` `_iniciarFadeIn()` ramps it 0.05 → `alarma.volumen`. Uses `AudioSessionConfiguration.music()` — normal media session, fully subject to device media volume. 3. **Handoff**: when the Flutter player reaches `reproduciendo`, `confirmarAudioFlutter` → `MainActivity` → `PluriWaveAlarmService.stop()`. Native audio torn down; Flutter media-stream player is the sole source for the rest of the ring. Grep-verified: **zero occurrences of `setStreamVolume`/AudioManager volume-override anywhere in the codebase, ever** — the app has never programmatically overridden device stream volume. The "used to ignore device volume" impression comes from the native track's brief immune window, not a regressed capability. ## Root Causes **A — architectural**: steady-state alarm audio is the Flutter media-stream player within 1-3s of firing. Media volume 0 → silent alarm (symptom 2). **B — manifest bug (verified)**: `AndroidManifest.xml:55-58` declares `foregroundServiceType="mediaPlayback|systemExempted"` and NO `FOREGROUND_SERVICE_ALARM` permission — contradicting the approved design (`app-quality-and-native-alarms` design.md Decision 1.1, spec S1-R1 CRITICAL, which mandates `mediaPlayback|alarm` + permission to prevent `ForegroundServiceTypeException` on Android 14+ when starting from a broadcast receiver). `apply-progress.md` marks T-S1-03/04 done with "DEVIATION (see below)" but no Batch-1 deviation section exists. On Android 14+ this plausibly kills the native service start silently — removing even the brief volume-immune window. **C — double fade-in**: native Kotlin ramp and Dart ramp run independently and can interleave at handoff → audible jump/reset (likely the real cause of "feels broken", symptom 1). ## Recommendation (sequenced, one change) 1. **Manifest fix**: restore `mediaPlayback|alarm` + `FOREGROUND_SERVICE_ALARM` per the already-approved design. Near-zero risk. 2. **Ring-scoped volume immunity (option c)**: at ring start, capture current media-stream volume; force it to an audible reference; ramp PLAYER volume 5% → `alarma.volumen`; restore original stream volume on stop/snooze/dismiss. Scoped strictly to the ring — must not touch normal radio listening. 3. **Fade-in dedup**: gate so only one ramp drives audible volume across the handoff. Deferred (flagged as follow-up, not bundled): option (b) — native-only audio for the whole ring (Flutter screen as pure UI). Architecturally purest, but a large rewrite across `pantalla_alarma_sonando.dart`, `PluriWaveAlarmService.kt`, and the MethodChannel surface. ## Affected Areas - `AndroidManifest.xml:56-58` — FGS type + missing permission (CRITICAL on API 34+) - `PluriWaveAlarmService.kt` — native fade-in OK for its scope; handoff teardown - `lib/servicios/servicio_audio.dart` — steady-state alarm source, no alarm-stream routing - `lib/pantallas/pantalla_alarma_sonando.dart` — Dart ramp; double-ramp at handoff - `lib/app.dart` — `_prearrancarAudioAlarma()` shrinks the immune window - `app-quality-and-native-alarms/apply-progress.md` — missing Batch-1 deviation note (doc gap) ## Risks - Kotlin/manifest edits cannot be compiled by agents (`flutter build` forbidden) — mandatory on-device verification by the user (this exact gap is how the manifest regression slipped through) - Any audio-session/volume change must be provably scoped to the ring and reverted — must not regress phone-call ducking (`ServicioAudioSession`, S3-R1) or normal listening - Native and Dart fade-ins duplicate the same algorithm/constants — flag for single-sourcing to prevent drift