feat(ble): unbondSmart() · 삭제 시 자동 재연결 + 오프라인 경고 팝업 (cloud-mvp 1982399 이식)

This commit is contained in:
2026-08-04 17:03:38 +09:00
parent 6007c29c9d
commit a992a88b86
4 changed files with 121 additions and 16 deletions
@@ -410,6 +410,60 @@ class BleManager private constructor(private val context: Context) {
debugLogger.info("DEVICE_UNREGISTERED $address")
}
/**
* 스마트 삭제 (2026-08-04): 미연결 상태에서 삭제 시 기기 재연결 후 msr? 전송.
*
* 배경: 기기가 오프라인일 때 unbondAndRemoveAddress() 는 폰 측 LTK 만 삭제 →
* 기기는 여전히 이전 LTK 를 기대 · 재페어링 시 PIN 불일치 · 사용자가 기기
* 15초 롱프레스로 강제 초기화해야만 재페어링 가능.
*
* 이 함수는:
* - 이미 연결됨 → disconnectAndUnbond() (msr? + 양쪽 removeBond) → onSuccess
* - 미연결 → timeoutMs 재연결 시도
* 성공 → disconnectAndUnbond() → onSuccess
* 실패 → onDeviceOffline() (UI 가 강제 삭제 결정)
*/
fun unbondSmart(
address: String,
timeoutMs: Long = 5_000L,
onSuccess: () -> Unit,
onDeviceOffline: () -> Unit,
) {
if (bluetoothGatt?.device?.address == address && isServiceReady.value) {
disconnectAndUnbond()
onSuccess()
return
}
debugLogger.info("UNBOND_SMART attempting reconnect for $address (timeout=${timeoutMs}ms)")
val adapter = bluetoothAdapter ?: run { onDeviceOffline(); return }
val device = runCatching { adapter.getRemoteDevice(address) }.getOrNull()
if (device == null) { onDeviceOffline(); return }
val timeoutRunnable = Runnable {
debugLogger.warn("UNBOND_SMART timeout · reconnect failed for $address")
try { bluetoothGatt?.disconnect(); bluetoothGatt?.close() } catch (_: Exception) {}
bluetoothGatt = null
isConnecting.value = false
onDeviceOffline()
}
val originalCallback = onConnectionStateChanged
onConnectionStateChanged = { connected ->
originalCallback?.invoke(connected)
if (connected && bluetoothGatt?.device?.address == address) {
handler.removeCallbacks(timeoutRunnable)
onConnectionStateChanged = originalCallback
debugLogger.info("UNBOND_SMART reconnected · sending msr? for full unbond")
disconnectAndUnbond()
onSuccess()
}
}
handler.postDelayed(timeoutRunnable, timeoutMs)
connectByAddress(address, address)
}
/**
* 저장된 기기 주소로 직접 연결. 스캔 결과에 안 잡혀도 OS bond 가 있으면 바로 됨.
* KnownDeviceStore 에서 골라 호출.