Await Telecom cleanup before restoring the previous radio session

This commit is contained in:
evgeny committed 2026-10-29 13:34:25 +01:00
1 parent 870f1b5914
commit e81138e3d1
3 files changed
+16 -8

No files matched your search

@@ -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
@@ -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<UtunConnection>()
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")
@@ -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)