From e81138e3d1a91fb5d4cc2d35a40fffd70614a76a Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 29 Oct 2026 02:31:06 +0100 Subject: [PATCH] Await Telecom cleanup before restoring the previous radio session --- .../java/com/utun/chat/data/CallController.kt | 1 + .../com/utun/chat/data/TelecomCallManager.kt | 19 ++++++++++++------- .../com/utun/chat/data/CallServiceHost.kt | 4 +++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallController.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallController.kt index 34bcf36d..c86dafd8 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallController.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallController.kt @@ -334,6 +334,7 @@ object CallController : TelecomCallManager.Listener { scope.launch { serviceReleased?.await() released?.join() + TelecomCallManager.awaitReleased() if (!coreReady || !NativeLib.isReady() || blocksMedia()) return@launch val group = radioToRestore ?: return@launch radioToRestore = null diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/TelecomCallManager.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/TelecomCallManager.kt index 12663c7f..f0b650bd 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/TelecomCallManager.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/TelecomCallManager.kt @@ -11,6 +11,8 @@ import android.telecom.PhoneAccountHandle import android.telecom.TelecomManager import com.utun.chat.ChatApplication import java.util.concurrent.ConcurrentHashMap +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first /** * Мост между жизненным циклом P2P-звонка (CallController) и Telecom. @@ -50,7 +52,7 @@ object TelecomCallManager { private val disconnected = mutableSetOf() private var cleanupQueued = false private var cleanupDelay = 250L - @Volatile private var retiring = false + private val retiring = MutableStateFlow(false) private val cleanupRetry = Runnable { cleanupQueued = false; cleanupConnections() } @Volatile private var phoneAccountHandle: PhoneAccountHandle? = null @@ -85,11 +87,14 @@ object TelecomCallManager { fun isReady(): Boolean = ready /** Не передавать глобальную аудиополитику другому media/core, пока SDK удерживает старый Connection. */ - fun cleanupPending(): Boolean = retiring + fun cleanupPending(): Boolean = retiring.value + + /** Ожидание не отменяет SDK cleanup; восстановление рации ждёт подтверждённого освобождения Telecom. */ + suspend fun awaitReleased() { retiring.first { !it } } fun requireReleased() { checkMain(); cleanupConnections() - check(!retiring) { + check(!retiring.value) { LogManager.addLog("ERROR", TAG, "Telecom cleanup still pending connections=${closing.size}") "Telecom connection cleanup pending" } @@ -208,12 +213,12 @@ object TelecomCallManager { checkMain() val conn = connections.remove(callId) if (conn != null) retire(callId, conn, reasons[conn] ?: 0) else cleanupConnections() - LogManager.addLog("DEBUG", TAG, "connection removal requested id=$callId pending=$retiring") + LogManager.addLog("DEBUG", TAG, "connection removal requested id=$callId pending=${retiring.value}") } private fun retire(callId: Long, conn: UtunConnection, reason: Int) { closing.putIfAbsent(conn, Closing(callId, reason, conn in disconnected)) - retiring = true + retiring.value = true LogManager.addLog("DEBUG", TAG, "connection retiring id=$callId owner=${System.identityHashCode(conn)} reason=$reason") reasons.remove(conn); disconnected.remove(conn); cleanupConnections() } @@ -230,8 +235,8 @@ object TelecomCallManager { LogManager.addLog("ERROR", TAG, "connection destroy failed id=${owner.id} owner=${System.identityHashCode(conn)}; retained: $e") } } - retiring = closing.isNotEmpty() - if (!retiring) { mainHandler.removeCallbacks(cleanupRetry); cleanupQueued = false; cleanupDelay = 250; return } + retiring.value = closing.isNotEmpty() + if (!retiring.value) { mainHandler.removeCallbacks(cleanupRetry); cleanupQueued = false; cleanupDelay = 250; return } if (!cleanupQueued) { cleanupQueued = mainHandler.postDelayed(cleanupRetry, cleanupDelay) if (!cleanupQueued) LogManager.addLog("ERROR", TAG, "connection cleanup retry rejected count=${closing.size}; owners retained") diff --git a/tools/chatgui-android/call-service-tests/src/main/kotlin/com/utun/chat/data/CallServiceHost.kt b/tools/chatgui-android/call-service-tests/src/main/kotlin/com/utun/chat/data/CallServiceHost.kt index 21588935..5704e255 100644 --- a/tools/chatgui-android/call-service-tests/src/main/kotlin/com/utun/chat/data/CallServiceHost.kt +++ b/tools/chatgui-android/call-service-tests/src/main/kotlin/com/utun/chat/data/CallServiceHost.kt @@ -63,9 +63,11 @@ class CallVideoEngine(context: Context, val id: Long) { } object TelecomCallManager { - var pending = false + private val closing = MutableStateFlow(false) + var pending: Boolean get() = closing.value; set(value) { closing.value = value } fun cleanupPending() = pending fun requireReleased() { check(!pending) { "Telecom connection cleanup pending" } } + suspend fun awaitReleased() { closing.first { !it } } interface Listener { fun onConnectionAvailable(callId: Long, connection: UtunConnection) fun onAnswer(callId: Long)