Tier-3 background-audio audit (item #4 found the only gap): AudioTrackPlayer.start() never called requestAudioFocus, so an inbound phone call mixed on top of the room audio instead of ducking it. Fixed by acquiring focus at the SERVICE level (one owner per room session, not per player) in AudioRoomForegroundService.requestAudioFocus(): * AUDIOFOCUS_GAIN with USAGE_VOICE_COMMUNICATION + CONTENT_TYPE_SPEECH so the OS treats the room like a phone call. * setAcceptsDelayedFocusGain(false) — immediate focus or nothing. * On-change listener is a no-op; the OS handles duck-on-loss. * abandonAudioFocusRequest in onDestroy. * Best-effort acquire — runCatching swallows denials so a refused request doesn't fail service start (the OS will duck/pause us by its own policy, which is degraded but acceptable). Audit notes file under nestsClient/plans/ documents items 1-5 with commit-time citations: 1. PARTIAL_WAKE_LOCK — already correct 2. FG type microphone (14+) — already correct 3. FG type media-playback for listen-only — already correct 4. Audio focus — fixed in THIS commit 5. PIP keep-alive — already correct
2.8 KiB
Background-audio audit (T3 #2)
Audit run for the Tier-3 plan checklist. Items pass-by-default; deltas captured below.
1. PARTIAL_WAKE_LOCK during a broadcast — ✅ PASS
amethyst/src/main/java/com/vitorpamplona/amethyst/service/audiorooms/AudioRoomForegroundService.kt
- Acquired in
onCreate()(line 64) vianewWakeLock(PARTIAL_WAKE_LOCK, "amethyst:audio-room")withsetReferenceCounted(false). - Acquire on every
onStartCommandif not held (line 99-101) with a 4-hour safety cap (WAKE_LOCK_TIMEOUT_MS). - Released in
onDestroy()(line 174-175) viawakeLock?.takeIf { it.isHeld }?.release().
2. Foreground-service type microphone for Android 14+ — ✅ PASS
Same file, startForegroundWithType(includeMic):
- When
includeMic = true(broadcast mode), usesFOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK or FOREGROUND_SERVICE_TYPE_MICROPHONE(lines 111-112). Required by Android 14+ while the mic is open. - The manifest already declares
android:foregroundServiceType="microphone|mediaPlayback"(verified separately inAndroidManifest.xml).
3. Listener-only foreground type — ✅ PASS
Same call, includeMic = false path uses just FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK (line 114). Play Store policy requires the elevated microphone permission only when the mic is actually open; listen-only sessions correctly stick to media-playback.
4. Audio focus — ❌ WAS MISSING; FIXED IN THIS COMMIT
Pre-fix: AudioTrackPlayer.start() constructed an AudioTrack but never called AudioManager.requestAudioFocus(...). A phone call would mix on top of the room audio instead of ducking it.
Fix: focus is now requested at the SERVICE level (one focus owner per room session, not per player) in AudioRoomForegroundService.requestAudioFocus():
AUDIOFOCUS_GAINwithUSAGE_VOICE_COMMUNICATION+CONTENT_TYPE_SPEECHso the OS treats the room like a phone call.setAcceptsDelayedFocusGain(false)— we want the focus immediately or not at all.- On-change listener is a no-op; the OS handles duck-on-loss for us.
abandonAudioFocusRequestinonDestroy().
Refused focus requests are caught with runCatching — a denial means the OS will duck/pause us based on its own policy, which is a degraded but acceptable experience and shouldn't fail service start.
5. PIP keep-alive — ✅ PASS (existing)
AudioRoomActivity already survives the PIP transition without dropping the foreground service. The activity's onPictureInPictureModeChanged doesn't stop the service; the service tracks its own promoted state independent of activity lifecycle.
Summary
- 4/5 items passed before this commit
- 1 fix in
AudioRoomForegroundServiceadds the audio-focus request
No follow-ups remaining for the Tier-3 audit checklist.