mirror of https://github.com/openclaw/openclaw.git
fix: serialize TalkModeManager player cleanup (#52310) (thanks @Kaneki-x)
* Android: fix MediaPlayer double-release race in TalkModeManager * Android: guard currentPosition read against concurrent player release * fix: serialize TalkModeManager player cleanup * fix: serialize TalkModeManager player cleanup (#52310) (thanks @Kaneki-x) --------- Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
parent
5cb2f45585
commit
c7788773bf
|
|
@ -243,6 +243,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Android/theme: switch status bar icon contrast with the active system theme so Android light mode no longer leaves unreadable light icons over the app header. (#51098) Thanks @goweii.
|
||||
- Discord/ACP: forward worker abort signals into ACP turns so timed-out Discord jobs cancel the running turn instead of silently leaving the bound ACP session working in the background.
|
||||
- Gateway/openresponses: preserve assistant commentary and session continuity across hosted-tool `/v1/responses` turns, and emit streamed tool-call payloads before finalization so client tool loops stay resumable. (#52171) Thanks @CharZhou.
|
||||
- Android/Talk: serialize `TalkModeManager` player teardown so rapid interrupt/restart cycles stop double-releasing or overlapping TTS playback. (#52310) Thanks @Kaneki-x.
|
||||
|
||||
### Breaking
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ class TalkModeManager(
|
|||
private val playbackGeneration = AtomicLong(0L)
|
||||
|
||||
private var ttsJob: Job? = null
|
||||
private val playerLock = Any()
|
||||
private var player: MediaPlayer? = null
|
||||
@Volatile private var finalizeInFlight = false
|
||||
private var listenWatchdogJob: Job? = null
|
||||
|
|
@ -763,7 +764,9 @@ class TalkModeManager(
|
|||
try {
|
||||
withContext(Dispatchers.IO) { tempFile.writeBytes(audioBytes) }
|
||||
val player = MediaPlayer()
|
||||
this.player = player
|
||||
synchronized(playerLock) {
|
||||
this.player = player
|
||||
}
|
||||
val finished = CompletableDeferred<Unit>()
|
||||
player.setAudioAttributes(
|
||||
AudioAttributes.Builder()
|
||||
|
|
@ -784,7 +787,7 @@ class TalkModeManager(
|
|||
ensurePlaybackActive(playbackToken)
|
||||
} finally {
|
||||
try {
|
||||
cleanupPlayer()
|
||||
cleanupPlayer(player)
|
||||
} catch (_: Throwable) {}
|
||||
tempFile.delete()
|
||||
}
|
||||
|
|
@ -821,7 +824,11 @@ class TalkModeManager(
|
|||
return
|
||||
}
|
||||
if (resetInterrupt) {
|
||||
val currentMs = player?.currentPosition?.toDouble() ?: 0.0
|
||||
val currentMs = synchronized(playerLock) {
|
||||
try {
|
||||
player?.currentPosition?.toDouble() ?: 0.0
|
||||
} catch (_: IllegalStateException) { 0.0 }
|
||||
}
|
||||
lastInterruptedAtSeconds = currentMs / 1000.0
|
||||
}
|
||||
cleanupPlayer()
|
||||
|
|
@ -864,10 +871,16 @@ class TalkModeManager(
|
|||
audioFocusRequest = null
|
||||
}
|
||||
|
||||
private fun cleanupPlayer() {
|
||||
player?.stop()
|
||||
player?.release()
|
||||
player = null
|
||||
private fun cleanupPlayer(expectedPlayer: MediaPlayer? = null) {
|
||||
synchronized(playerLock) {
|
||||
val p = player ?: return
|
||||
if (expectedPlayer != null && p !== expectedPlayer) return
|
||||
player = null
|
||||
try {
|
||||
p.stop()
|
||||
} catch (_: IllegalStateException) {}
|
||||
p.release()
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldInterrupt(transcript: String): Boolean {
|
||||
|
|
|
|||
Loading…
Reference in New Issue