Guard call audio ownership on main and avoid touching released devices
This commit is contained in:
1 parent
0340a59b15
commit
326bfd124e
2 files changed
+20
-5
No files matched your search
@@ -134,6 +134,7 @@ class CallAudioEngine {
|
||||
val am = ChatApplication.instance.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
am.mode = AudioManager.MODE_NORMAL
|
||||
if (Build.VERSION.SDK_INT >= 31) am.clearCommunicationDevice()
|
||||
modeOwned = false
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("WARN", "CallAudio", "release fallback routing failed id=$callId: ${e.message}")
|
||||
}
|
||||
@@ -157,8 +158,17 @@ class CallAudioEngine {
|
||||
fun isMuted(): Boolean = muted
|
||||
|
||||
/** Main: остановить прежний запуск, подготовить маршрут и запустить независимые RX/TX.
|
||||
* true — workers запущены; ошибки внутри них приходят через onFailure. false — синхронный отказ старта. */
|
||||
* true — workers запущены; ошибки внутри них приходят через onFailure. false — синхронный отказ старта.
|
||||
* Ошибка очистки прежнего/частичного запуска бросается владельцу с сохранёнными ресурсами. id не равен нулю. */
|
||||
fun start(id: Long): Boolean {
|
||||
check(Looper.myLooper() == Looper.getMainLooper()) {
|
||||
LogManager.addLog("ERROR", "CallAudio", "start outside main id=$id")
|
||||
"Call audio start requires main"
|
||||
}
|
||||
require(id != 0L) {
|
||||
LogManager.addLog("ERROR", "CallAudio", "start rejected: zero call id")
|
||||
"Call id must be nonzero"
|
||||
}
|
||||
stop()
|
||||
startElapsedMs = SystemClock.elapsedRealtime()
|
||||
|
||||
@@ -287,7 +297,7 @@ class CallAudioEngine {
|
||||
"nativeMs=$nativeMs modeMs=$modeMs routeMs=$routeMs")
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "CallAudio", "start failed id=$id: ${e.message}")
|
||||
LogManager.addLog("ERROR", "CallAudio", "start failed id=$id: $e")
|
||||
stop()
|
||||
return false
|
||||
}
|
||||
@@ -316,6 +326,10 @@ class CallAudioEngine {
|
||||
* Join ограничен 250 мс на worker; живые потоки и устройства остаются до следующей попытки.
|
||||
* При неполной очистке бросает ошибку владельцу; native останавливается лишь после устройств. */
|
||||
fun stop() {
|
||||
check(Looper.myLooper() == Looper.getMainLooper()) {
|
||||
LogManager.addLog("ERROR", "CallAudio", "stop outside main id=$callId")
|
||||
"Call audio stop requires main"
|
||||
}
|
||||
if (callId == 0L) return
|
||||
val id = callId
|
||||
active = false
|
||||
@@ -335,13 +349,13 @@ class CallAudioEngine {
|
||||
}
|
||||
}
|
||||
synchronized(recordLock) {
|
||||
audioRecord?.let { rec ->
|
||||
audioRecord?.takeIf { captureThread != null }?.let { rec ->
|
||||
try { if (rec.recordingState == AudioRecord.RECORDSTATE_RECORDING) rec.stop() }
|
||||
catch (e: Exception) { LogManager.addLog("ERROR", "CallAudio", "record stop failed id=$id: $e") }
|
||||
}
|
||||
}
|
||||
synchronized(trackLock) {
|
||||
audioTrack?.let { track ->
|
||||
audioTrack?.takeIf { playThread != null }?.let { track ->
|
||||
try { track.removeOnRoutingChangedListener(trackRouteListener) }
|
||||
catch (e: Exception) { LogManager.addLog("WARN", "CallAudio", "track listener removal failed id=$id: $e") }
|
||||
try { if (track.playState == AudioTrack.PLAYSTATE_PLAYING) track.pause() }
|
||||
|
||||
@@ -90,7 +90,8 @@ internal class CallAudioOutput private constructor(
|
||||
|
||||
companion object {
|
||||
/** AAudio пробуется лишь при tryFast и deviceId встроенного speaker из communication devices API 31+.
|
||||
* Native проверяет формат/latency/burst/device; отказ возвращает AudioTrack, сбой fallback бросает исключение. */
|
||||
* Native проверяет формат/latency/burst/device; отказ возвращает AudioTrack, сбой fallback бросает исключение.
|
||||
* Владелец сохраняет возвращённый выход до проверки track.state и дальнейшей настройки, включая отказ release. */
|
||||
fun open(manager: AudioManager, deviceId: Int, tryFast: Boolean): CallAudioOutput {
|
||||
val type = if (Build.VERSION.SDK_INT >= 31)
|
||||
manager.availableCommunicationDevices.firstOrNull { it.id == deviceId }?.type else null
|
||||
|
||||
Reference in new issue
Block a user