fix: BLE reconnect를 fresh scan→connect 방식으로 변경
기존: connectGatt(autoConnect=true) — Android에서 매우 불안정, 수분 걸림 변경: BLE 스캔으로 기기를 다시 찾은 후 connectGatt(autoConnect=false)로 빠르게 연결 재연결 흐름: 1. watchdog 15초 타임아웃 → forceDisconnectAndReconnect() 2. scheduleAutoReconnect() → 3s/5s/10s 백오프 3. attemptAutoReconnect(): - BLE 스캔 시작 (기기 이름 or MAC 주소 매칭) - 발견 시 즉시 connectGatt(autoConnect=false) → 빠른 연결 - 8초 스캔 타임아웃 → 다음 attempt - 최대 5회 시도 후 실패 처리 4. 모든 단계 BleDebugLogger에 기록 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -418,8 +418,11 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
handler.postDelayed(timer, delay)
|
handler.postDelayed(timer, delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var reconnectScanCallback: ScanCallback? = null
|
||||||
|
|
||||||
private fun attemptAutoReconnect() {
|
private fun attemptAutoReconnect() {
|
||||||
val address = lastConnectedAddress ?: return
|
val targetAddress = lastConnectedAddress ?: return
|
||||||
|
val targetName = debugLogger.deviceName
|
||||||
if (bluetoothAdapter?.isEnabled != true) {
|
if (bluetoothAdapter?.isEnabled != true) {
|
||||||
onReconnectionFailed?.invoke()
|
onReconnectionFailed?.invoke()
|
||||||
isReconnecting.value = false
|
isReconnecting.value = false
|
||||||
@@ -427,15 +430,69 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reconnectAttempt++
|
reconnectAttempt++
|
||||||
logd { "Attempting auto-reconnect #$reconnectAttempt to $address" }
|
logd { "Auto-reconnect #$reconnectAttempt: scanning for $targetName ($targetAddress)" }
|
||||||
|
debugLogger.info("RECONNECT_SCAN #$reconnectAttempt scanning for $targetName")
|
||||||
|
|
||||||
bluetoothGatt?.let { gatt ->
|
bluetoothGatt?.let { gatt ->
|
||||||
try { gatt.close() } catch (_: Exception) {}
|
try { gatt.close() } catch (_: Exception) {}
|
||||||
bluetoothGatt = null
|
bluetoothGatt = null
|
||||||
}
|
}
|
||||||
|
|
||||||
val device = bluetoothAdapter.getRemoteDevice(address)
|
// Fresh scan to find the device again
|
||||||
bluetoothGatt = device.connectGatt(context, true, gattCallback, BluetoothDevice.TRANSPORT_LE)
|
val scanner = bluetoothAdapter.bluetoothLeScanner
|
||||||
|
if (scanner == null) {
|
||||||
|
scheduleAutoReconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any previous reconnect scan
|
||||||
|
reconnectScanCallback?.let {
|
||||||
|
try { scanner.stopScan(it) } catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
val callback = object : ScanCallback() {
|
||||||
|
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||||
|
val name = try { result.device.name ?: "" } catch (_: Exception) { "" }
|
||||||
|
val address = result.device.address
|
||||||
|
|
||||||
|
if (address == targetAddress || (targetName.isNotEmpty() && name == targetName)) {
|
||||||
|
logd { "Reconnect scan found: $name ($address)" }
|
||||||
|
debugLogger.info("RECONNECT_FOUND $name ($address) RSSI=${result.rssi}")
|
||||||
|
|
||||||
|
try { scanner.stopScan(this) } catch (_: Exception) {}
|
||||||
|
reconnectScanCallback = null
|
||||||
|
|
||||||
|
// Connect directly (autoConnect=false for fast connection)
|
||||||
|
bluetoothGatt = result.device.connectGatt(
|
||||||
|
context, false, gattCallback, BluetoothDevice.TRANSPORT_LE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reconnectScanCallback = callback
|
||||||
|
|
||||||
|
try {
|
||||||
|
scanner.startScan(callback)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
debugLogger.error("RECONNECT_SCAN_FAIL ${e.message}")
|
||||||
|
scheduleAutoReconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop scan after 8 seconds → retry next attempt
|
||||||
|
handler.postDelayed({
|
||||||
|
try { scanner.stopScan(callback) } catch (_: Exception) {}
|
||||||
|
reconnectScanCallback = null
|
||||||
|
|
||||||
|
if (!isConnected.value && reconnectAttempt < maxReconnectAttempts) {
|
||||||
|
debugLogger.warn("RECONNECT_SCAN_TIMEOUT #$reconnectAttempt — device not found")
|
||||||
|
scheduleAutoReconnect()
|
||||||
|
} else if (!isConnected.value) {
|
||||||
|
debugLogger.reconnectFailed()
|
||||||
|
isReconnecting.value = false
|
||||||
|
onReconnectionFailed?.invoke()
|
||||||
|
}
|
||||||
|
}, 8000)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelReconnectTimer() {
|
private fun cancelReconnectTimer() {
|
||||||
|
|||||||
Reference in New Issue
Block a user