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 4c8a7fce..683987f8 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 @@ -328,7 +328,7 @@ object CallController : TelecomCallManager.Listener { _state.value = st.copy(phase = CallPhase.ENDED, reason = reason, preparing = false, connectionType = CallConnectionType.NONE, connectionNextHop = 0) syncCoreActivity() - LogManager.addLog("INFO", "Call", "finished id=$id reason=$reason serviceCleanupComplete=${serviceReleased?.isCompleted != false}") + LogManager.addLog("INFO", "Call", "finished id=$id reason=$reason serviceCleanupComplete=${!CallService.cleanupPending()}") val released = mediaPreparation scope.launch { serviceReleased?.await() diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallService.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallService.kt index 90f12529..d9c6ecde 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallService.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallService.kt @@ -47,7 +47,8 @@ class CallService : Service() { fun requireReleased() { checkMain() val previous = pendingCleanup ?: return - check(previous.shutdown().isCompleted) { + previous.shutdown() + check(pendingCleanup == null) { val message = "Call service cleanup still pending id=${previous.callId}" LogManager.addLog("ERROR", "CallService", message) message @@ -170,7 +171,7 @@ class CallService : Service() { } return START_NOT_STICKY } - if (stopping && !released.isCompleted) { + if (stopping && pendingCleanup === this) { LogManager.addLog("ERROR", "CallService", "start blocked by pending cleanup id=$id owned=$callId") try { CallController.audioFailed(id, "previous service cleanup pending") } catch (e: Exception) { LogManager.addLog("ERROR", "CallService", "cleanup refusal callback failed id=$id: $e") @@ -332,7 +333,7 @@ class CallService : Service() { return builder.build() } - /** На main независимо очистить владельцев. Deferred завершается только после подтверждения всех операций. + /** На main независимо очистить владельцев. Deferred успешно завершается после подтверждения всех операций. * При отказе сохраняется один process-владелец и отменяемый retry 250мс..30с; main не ждёт Deferred. */ fun shutdown(): Deferred { checkMain() @@ -356,7 +357,11 @@ class CallService : Service() { val pending = audioOwned || video != null || wakeLock != null || powerEventsRegistered || foregroundOwned || openIntent != null || endIntent != null || !stopRequested if (pending) { - check(pendingCleanup == null || pendingCleanup === this) { "Two retained call service owners" } + check(pendingCleanup == null || pendingCleanup === this) { + val message = "Two retained call service owners: current=$callId previous=${pendingCleanup?.callId}" + LogManager.addLog("ERROR", "CallService", message) + message + } pendingCleanup = this val delay = retryDelayMs retryDelayMs = (retryDelayMs * 2).coerceAtMost(30_000L) @@ -428,7 +433,7 @@ class CallService : Service() { try { CallController.serviceLost(id, this) } catch (e: Exception) { LogManager.addLog("ERROR", "CallService", "service lost callback failed id=$id: $e") } - LogManager.addLog("INFO", "CallService", "destroyed id=$id cleanupComplete=${released.isCompleted}") + LogManager.addLog("INFO", "CallService", "destroyed id=$id cleanupComplete=${pendingCleanup !== this}") } finally { super.onDestroy() } }