From b749733289aab6c4500573e0cf756dc02b653210 Mon Sep 17 00:00:00 2001 From: QIU Date: Mon, 17 Aug 2026 16:57:33 +0000 Subject: [PATCH] fix(audio): keep cleanup from masking start failures or skipping release AudioCapture.audioFlow's finally ran recorder.stop() then release() naked. If startRecording() threw - permission revoked mid-request, audio device error - the finally's stop() threw IllegalStateException (stop on an uninitialized recorder), which replaced the original error AND skipped release(), leaking the AudioRecord. The flow's caller saw "recorder failure" instead of "no permission" and the native recorder was never freed. Wrapping each cleanup step in runCatching preserves the original exception while guaranteeing release() runs. Probe: a start failure previously surfaced as RuntimeError with released=false; it now surfaces as the original PermissionError with released=true. --- .../look4sat/core/data/usecase/AudioCapture.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/data/src/main/java/com/rtbishop/look4sat/core/data/usecase/AudioCapture.kt b/core/data/src/main/java/com/rtbishop/look4sat/core/data/usecase/AudioCapture.kt index 98d3ce66..87907a63 100644 --- a/core/data/src/main/java/com/rtbishop/look4sat/core/data/usecase/AudioCapture.kt +++ b/core/data/src/main/java/com/rtbishop/look4sat/core/data/usecase/AudioCapture.kt @@ -56,8 +56,14 @@ class AudioCapture : IAudioCapture { if (read > 0) emit(if (read == chunkSize) buffer.copyOf() else buffer.copyOfRange(0, read)) } } finally { - recorder.stop() - recorder.release() + // stop() on a recorder that never started throws + // IllegalStateException; wrapping each cleanup step separately + // keeps the original error (e.g. a permission denial during + // startRecording) intact and guarantees release() still runs. + // Without this, a start failure masked the real cause AND leaked + // the recorder because release() was skipped. + runCatching { recorder.stop() } + runCatching { recorder.release() } } }.flowOn(Dispatchers.IO) }