Критический фикс отправки после верификации устройства
Some checks failed
Android Kernel Build / build (push) Has been cancelled
Some checks failed
Android Kernel Build / build (push) Has been cancelled
This commit is contained in:
@@ -311,14 +311,35 @@ class Protocol(
|
|||||||
when (resolve.solution) {
|
when (resolve.solution) {
|
||||||
DeviceResolveSolution.ACCEPT -> {
|
DeviceResolveSolution.ACCEPT -> {
|
||||||
log("✅ DEVICE VERIFICATION ACCEPTED (deviceId=${shortKey(resolve.deviceId, 12)})")
|
log("✅ DEVICE VERIFICATION ACCEPTED (deviceId=${shortKey(resolve.deviceId, 12)})")
|
||||||
if (_state.value == ProtocolState.DEVICE_VERIFICATION_REQUIRED) {
|
val stateAtAccept = _state.value
|
||||||
|
if (stateAtAccept == ProtocolState.AUTHENTICATED) {
|
||||||
|
log("✅ ACCEPT ignored: already authenticated")
|
||||||
|
return@waitPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateAtAccept == ProtocolState.DEVICE_VERIFICATION_REQUIRED) {
|
||||||
setState(ProtocolState.CONNECTED, "Device verification accepted")
|
setState(ProtocolState.CONNECTED, "Device verification accepted")
|
||||||
val publicKey = lastPublicKey
|
}
|
||||||
val privateHash = lastPrivateHash
|
|
||||||
if (!publicKey.isNullOrBlank() && !privateHash.isNullOrBlank()) {
|
val publicKey = lastPublicKey
|
||||||
|
val privateHash = lastPrivateHash
|
||||||
|
if (publicKey.isNullOrBlank() || privateHash.isNullOrBlank()) {
|
||||||
|
log("⚠️ ACCEPT received but credentials are missing, waiting for reconnect")
|
||||||
|
return@waitPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
when (_state.value) {
|
||||||
|
ProtocolState.DISCONNECTED -> {
|
||||||
|
log("🔄 ACCEPT while disconnected -> reconnecting")
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolState.CONNECTING -> {
|
||||||
|
log("⏳ ACCEPT while connecting -> waiting for onOpen auto-handshake")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
startHandshake(publicKey, privateHash, lastDevice)
|
startHandshake(publicKey, privateHash, lastDevice)
|
||||||
} else {
|
|
||||||
log("⚠️ ACCEPT received but credentials are missing, waiting for reconnect")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -644,7 +665,14 @@ class Protocol(
|
|||||||
val currentState = _state.value
|
val currentState = _state.value
|
||||||
val socket = webSocket
|
val socket = webSocket
|
||||||
val socketReady = socket != null
|
val socketReady = socket != null
|
||||||
val authReady = handshakeComplete && currentState == ProtocolState.AUTHENTICATED
|
val authReady = currentState == ProtocolState.AUTHENTICATED
|
||||||
|
if (authReady && !handshakeComplete) {
|
||||||
|
// Defensive self-heal:
|
||||||
|
// AUTHENTICATED state must imply completed handshake.
|
||||||
|
// If these flags diverge, message sending can be stuck in queue forever.
|
||||||
|
log("⚠️ AUTHENTICATED with handshakeComplete=false -> self-heal handshakeComplete=true")
|
||||||
|
handshakeComplete = true
|
||||||
|
}
|
||||||
val preAuthAllowedPacket =
|
val preAuthAllowedPacket =
|
||||||
packet is PacketSignalPeer || packet is PacketWebRTC || packet is PacketIceServers
|
packet is PacketSignalPeer || packet is PacketWebRTC || packet is PacketIceServers
|
||||||
val preAuthReady =
|
val preAuthReady =
|
||||||
@@ -773,6 +801,13 @@ class Protocol(
|
|||||||
val previousState = _state.value
|
val previousState = _state.value
|
||||||
log("🔌 DISCONNECT HANDLER: previousState=$previousState, manuallyClosed=$isManuallyClosed, reconnectAttempts=$reconnectAttempts, isConnecting=$isConnecting")
|
log("🔌 DISCONNECT HANDLER: previousState=$previousState, manuallyClosed=$isManuallyClosed, reconnectAttempts=$reconnectAttempts, isConnecting=$isConnecting")
|
||||||
|
|
||||||
|
// Duplicate callbacks are possible (e.g. heartbeat failure + onFailure/onClosed).
|
||||||
|
// If we are already disconnected and a reconnect is pending, avoid scheduling another one.
|
||||||
|
if (previousState == ProtocolState.DISCONNECTED && reconnectJob?.isActive == true) {
|
||||||
|
log("⚠️ DISCONNECT DUPLICATE: reconnect already scheduled, skipping")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// КРИТИЧНО: если уже идет подключение, не делаем ничего
|
// КРИТИЧНО: если уже идет подключение, не делаем ничего
|
||||||
if (isConnecting) {
|
if (isConnecting) {
|
||||||
log("⚠️ DISCONNECT IGNORED: connection already in progress")
|
log("⚠️ DISCONNECT IGNORED: connection already in progress")
|
||||||
@@ -799,12 +834,16 @@ class Protocol(
|
|||||||
// КРИТИЧНО: отменяем предыдущий reconnect job если есть
|
// КРИТИЧНО: отменяем предыдущий reconnect job если есть
|
||||||
reconnectJob?.cancel()
|
reconnectJob?.cancel()
|
||||||
|
|
||||||
// Экспоненциальная задержка: 1s, 2s, 4s, 8s, максимум 30s
|
// Экспоненциальная задержка: 1s, 2s, 4s, 8s, 16s, максимум 30s.
|
||||||
val delayMs = minOf(1000L * (1 shl minOf(reconnectAttempts - 1, 4)), 30000L)
|
// IMPORTANT: reconnectAttempts may be 0 right after AUTHENTICATED reset.
|
||||||
log("🔄 SCHEDULING RECONNECT: attempt #$reconnectAttempts, delay=${delayMs}ms")
|
// Using (1 shl -1) causes overflow (seen in logs as -2147483648000ms).
|
||||||
|
val nextAttemptNumber = (reconnectAttempts + 1).coerceAtLeast(1)
|
||||||
|
val exponent = (nextAttemptNumber - 1).coerceIn(0, 4)
|
||||||
|
val delayMs = minOf(1000L * (1L shl exponent), 30000L)
|
||||||
|
log("🔄 SCHEDULING RECONNECT: attempt #$nextAttemptNumber, delay=${delayMs}ms")
|
||||||
|
|
||||||
if (reconnectAttempts > 20) {
|
if (nextAttemptNumber > 20) {
|
||||||
log("⚠️ WARNING: Too many reconnect attempts ($reconnectAttempts), may be stuck in loop")
|
log("⚠️ WARNING: Too many reconnect attempts ($nextAttemptNumber), may be stuck in loop")
|
||||||
}
|
}
|
||||||
|
|
||||||
reconnectJob = scope.launch {
|
reconnectJob = scope.launch {
|
||||||
|
|||||||
Reference in New Issue
Block a user