From 3ef245fc0ac38f8657d297dd3b18af3758f16c8a Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 28 Oct 2026 23:25:11 +0100 Subject: [PATCH] Verify pending router cleanup through repeatable stop calls --- .../main/java/com/utun/chat/data/CallAudioRouter.kt | 6 ++++-- .../com/utun/chat/data/CallAudioRouterRoutingTest.kt | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioRouter.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioRouter.kt index 989a7e7d..dff3df1b 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioRouter.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioRouter.kt @@ -525,8 +525,10 @@ class CallAudioRouter { val accepted = adapter.getProfileProxy(ChatApplication.instance, object : BluetoothProfile.ServiceListener { override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { synchronized(this@CallAudioRouter) { - if (proxy in received && proxy !== btHeadset && proxy !in btProxies) { - LogManager.addLog("WARN", "CallAudioRouter", "released BT proxy callback ignored proxy=$proxy generation=$generation") + if (proxy in received && proxy !== btHeadset) { + LogManager.addLog("WARN", "CallAudioRouter", "retired BT proxy callback ignored proxy=$proxy generation=$generation") + releaseBtProxies() + scheduleCleanup() return } received.add(proxy) diff --git a/tools/chatgui-android/router-tests/src/test/kotlin/com/utun/chat/data/CallAudioRouterRoutingTest.kt b/tools/chatgui-android/router-tests/src/test/kotlin/com/utun/chat/data/CallAudioRouterRoutingTest.kt index f9598b78..ecdb5cc1 100644 --- a/tools/chatgui-android/router-tests/src/test/kotlin/com/utun/chat/data/CallAudioRouterRoutingTest.kt +++ b/tools/chatgui-android/router-tests/src/test/kotlin/com/utun/chat/data/CallAudioRouterRoutingTest.kt @@ -108,12 +108,13 @@ class CallAudioRouterRoutingTest { BluetoothAdapter.current = adapter router.start(); adapter.connect(0) audio.failures = setOf("unregister after") - router.stop() + assertThrows(IllegalStateException::class.java) { router.stop() } assertTrue(adapter.live.isEmpty()) assertTrue(audio.callbacks.isEmpty()) assertNull(audio.communicationDevice) assertNull(router.currentOutputDevice) assertFalse(router.btInputAvailable) + assertTrue(router.cleanupPending) diagnostic("device callback unregister failed") } @@ -122,15 +123,15 @@ class CallAudioRouterRoutingTest { val callback = audio.callbacks.single() audio.failures = setOf("unregister") try { - router.stop() + assertThrows(IllegalStateException::class.java) { router.stop() } val count = audio.operations.size callback.onAudioDevicesAdded(arrayOf(wired)) assertEquals(count, audio.operations.size) assertNull(audio.communicationDevice) assertNull(router.currentOutputDevice) } finally { - // Имитация отказавшей ОС оставляет регистрацию: убрать только fixture после проверки best-effort stop. - audio.failures = emptySet(); audio.unregisterAudioDeviceCallback(callback) + audio.failures = emptySet(); router.stop() + assertTrue(audio.callbacks.isEmpty()); assertFalse(router.cleanupPending) } diagnostic("device callback unregister failed") } @@ -140,7 +141,7 @@ class CallAudioRouterRoutingTest { router.setAutoHeadset(true); router.start() assertTrue(router.headsetAvailable) audio.failures = setOf("clear") - router.stop() + assertThrows(IllegalStateException::class.java) { router.stop() } assertNull(router.currentOutputDevice); assertNull(router.currentInputDevice) assertFalse(router.headsetAvailable); assertFalse(router.wiredInputAvailable); assertFalse(router.btInputAvailable) assertTrue(audio.callbacks.isEmpty())