fix: watchdog을 별도 Thread로 분리 (handler 블로킹 방지)

기존: handler.postDelayed로 watchdog 실행 → BLE 스택이 handler를
블로킹하면 watchdog도 멈춤 (좀비 상태 감지 불가)

변경: 별도 daemon Thread에서 5초마다 lastRxTimestamp 체크
→ BLE handler가 블로킹되어도 독립적으로 15초 타임아웃 감지
→ 감지 시 handler.post로 메인 스레드에서 forceDisconnectAndReconnect 실행

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 14:09:16 +09:00
parent 9d32d13452
commit 988fbd9dbd
@@ -352,31 +352,39 @@ class BleManager private constructor(private val context: Context) {
batteryTimer = null
}
// ── Watchdog (zombie connection detection) ──
// ── Watchdog (zombie connection detection, runs on separate thread) ──
private var watchdogJob: Thread? = null
@Volatile private var watchdogRunning = false
private fun startWatchdog() {
stopWatchdog()
lastRxTimestamp = System.currentTimeMillis()
val check = object : Runnable {
override fun run() {
if (!isConnected.value) return
watchdogRunning = true
watchdogJob = Thread({
while (watchdogRunning) {
try { Thread.sleep(5000) } catch (_: InterruptedException) { break }
if (!watchdogRunning) break
if (!isConnected.value) continue
val silenceMs = System.currentTimeMillis() - lastRxTimestamp
if (silenceMs > watchdogTimeoutMs) {
handler.post {
logw { "Watchdog: no RX for ${silenceMs}ms — forcing disconnect + reconnect" }
debugLogger.error("WATCHDOG_TIMEOUT silence=${silenceMs}ms — forcing reconnect")
forceDisconnectAndReconnect()
return
}
handler.postDelayed(this, 5000)
break
}
}
watchdogTimer = check
handler.postDelayed(check, 5000)
}, "BLE-Watchdog").also { it.isDaemon = true; it.start() }
}
private fun stopWatchdog() {
watchdogTimer?.let { handler.removeCallbacks(it) }
watchdogTimer = null
watchdogRunning = false
watchdogJob?.interrupt()
watchdogJob = null
}
private fun forceDisconnectAndReconnect() {