[Android] AudioRecorder reopens a disconnected input stream with no limit: a re-route on every open (Samsung, concurrent capture) loops hundreds of times a minute and records nothing
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 48/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- android, cpp, react-native
- Ambito
- audio-video-rtc, mobile
Direzione di ricerca
Start with packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp, following onErrorAfterClose to restartCapture() to understand the repeated reopen path. Review how recorder state and stop, pause, and resume are handled before deciding how bounded retries and capture-down reporting fit the API. Done means repeated disconnects no longer cause an unbounded reopen loop, and the recording session can still be controlled and finalized while retries wait.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Description
On Android, AudioRecorder reopens its input stream on every ErrorDisconnected with no delay or limit (AndroidAudioRecorder::onErrorAfterClose → restartCapture()). When the system moves the stream to a different input device right after every open, this becomes a tight open/disconnect loop: the recorder reopens hundreds of times a minute, writes no audio, and nothing reaches JS (no error, no event), so the app keeps showing an active recording that saves nothing.
We hit it on Samsung phones when the app runs a second capture client at the same time (an RTC SDK, Agora, capturing with MIC) and the recorder uses the default input preset (VOICE_RECOGNITION):
- The recorder's stream opens on
Built-In Mic. - ~20 ms later Samsung's audio policy re-routes the shared input to
Built-In 2 Mic(AUDIO_DEVICE_IN_2MIC, a Samsung vendor device), because a shared input is routed for the highest-priority active source andMICoutranksVOICE_RECOGNITION. - AAudio turns any device change on a running stream into a disconnect (
AudioStreamLegacy::onAudioDeviceUpdate— same rule in Android 13–16; the MMAP path disconnects on a device change too). restartCapture()reopens immediately into the same re-route. Repeat.
APM_AudioPolicyManager: getInputForAttr() … Source: AUDIO_SOURCE_VOICE_RECOGNITION … selectedDeviceId 19
AudioStreamLegacy: onAudioDeviceUpdate() request DISCONNECT in data callback, device 19 => 22
AudioRecord: processAudioBuffer(): EVENT_MORE_DATA requested 7680 bytes but callback returned -1 bytes
AAudio: AAudioStream_requestStop(s#1) called
… (repeats ~740×/min for the whole session)
Observed on a Galaxy S24 Ultra (Android 16) and a Galaxy S20 FE (Android 13). Pixels map both sources to the same mic, so they don't reproduce it.
Where this was reproduced vs. checked: we reproduced it on our fork, which is based on 0.13.6 and opens the input Shared / PerformanceMode::None. In 1.0.0 (ce354a54) and current main (96afff59) the same unbounded path is still there: onErrorAfterClose calls restartCapture() on every ErrorDisconnected, and restartCapture() has no delay or attempt limit. Those versions open Exclusive, but with another capture client active the input falls back to shared, and the disconnect rule applies on both paths, so we expect the same loop. We haven't run 1.0.0 on the Samsung devices yet.
Workaround: pass androidInputPreset: 'generic' (MIC) so both clients share one device. We confirmed this on the S20 FE: 0 disconnects over an 8.5-minute recording with the RTC SDK active.
Suggested fix: bound the reclaim. For example, treat a stream that dies within ~1 s of starting as a fast disconnect, reopen immediately for the first couple, then back off exponentially (we use 250 ms doubling to a 5 s cap) and reset once a reopened stream survives. Also tell JS that capture is down while backing off: an error, or a recorder event or state change, whichever fits the library's API. Keep the session's state (recording/paused, outputs) while it waits, so stop() during the wait still finalizes the file and pause()/resume() still apply. We have this working in our fork (gtest-covered policy, waits on oboe's error thread on a condition variable that stop()/start() cancel). We're happy to open a PR against main if you agree with the approach and tell us how you'd like the "capture down" state surfaced.
Steps to reproduce
- On a Samsung Android phone, start an in-app capture client with
MIC(e.g. join an Agora channel as broadcaster withAudioScenarioGameStreaming). - While it is capturing, start an
AudioRecorderwith file output and noandroidInputPreset(defaultVOICE_RECOGNITION). - Watch
adb logcat | grep -E "onAudioDeviceUpdate|AAudioStream_requestStop": aDISCONNECT … X => Yand a stop on every open, continuously. - Stop the recorder: the file holds well under a second of audio, captured only after the other client released the mic.
Snack or a link to a repository
No public repro yet. It needs a Samsung device and a second capture client in the same app; the code path is packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp (onErrorAfterClose → restartCapture). We can share a minimal repro app if that helps.
React Native Audio API version
Reproduced on a 0.13.6-based fork; unbounded restart confirmed by code in 1.0.0 and main (96afff59).
React Native version
0.88.1
Platforms
Android
JavaScript runtime
Hermes
Workflow
Expo Dev Client
Architecture
Fabric (New Architecture)
Build type
Debug and release
Device
Real device
Device model
Samsung Galaxy S24 Ultra (SM-S928U1, Android 16); Samsung Galaxy S20 FE 5G (SM-G781U1, Android 13)
- Lingua principale
- C++
- Stelle
- 856
- Fork
- 100
- Merge medio
- 2g 1h
- PR unite (30g)
- 34
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di software-mansion/react-native-audio-api
-
Android: audio focus listener posts to a dead looper after JS reload, interruptions (calls) are missedForse già presa @JaydeepAgravat l’ha presa 1 giorno fa. Apertaandroid bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
software-mansion/react-native-audio-api#1358 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
android bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
software-mansion/react-native-audio-api#1273 · 5 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
bug ios
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
software-mansion/react-native-audio-api#1238 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 57/100
software-mansion/react-native-audio-api#1418 ·
I maintainer di solito rispondono entro 1 giorno
-
feature-request ios
Difficoltà 5/5 Più di una settimana Idoneità per principianti 45/100
software-mansion/react-native-audio-api#1398 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di software-mansion/react-native-audio-api
Issue simili
-
parse_selu lowering loses catastrophic cancellation → silent 0.0 for moderate positive inputsAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
ROCm/AMDMIGraphX#5490 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
arvidn/libtorrent#9126 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
Sunrisepeak/mcpp-language-server#80 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
xiaoyifang/goldendict-ng#3148 ·
I maintainer di solito rispondono entro 1 giorno