From 64c795f2331c63e55a2cc4342f9f5f6c63036ceb Mon Sep 17 00:00:00 2001 From: Mikhail Date: Fri, 15 May 2026 13:34:54 +0300 Subject: [PATCH] Make Android VPN watchdog conservative under load --- clients/android/app/build.gradle | 4 +-- .../java/su/cin/rapvpn/RapVpnService.java | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/clients/android/app/build.gradle b/clients/android/app/build.gradle index 3c2c11a..c5c0321 100644 --- a/clients/android/app/build.gradle +++ b/clients/android/app/build.gradle @@ -30,8 +30,8 @@ android { applicationId "su.cin.rapvpn" minSdk 26 targetSdk 35 - versionCode 194 - versionName "0.2.194" + versionCode 195 + versionName "0.2.195" buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\"" buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\"" buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\"" diff --git a/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java b/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java index 21403c7..e42b326 100644 --- a/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java +++ b/clients/android/app/src/main/java/su/cin/rapvpn/RapVpnService.java @@ -78,6 +78,9 @@ public class RapVpnService extends VpnService { private static final int RUNTIME_STATUS_INTERVAL_MS = 500; private static final int RUNTIME_WATCHDOG_INTERVAL_MS = 2000; private static final int RUNTIME_WATCHDOG_STALE_SYNACK_MS = 7000; + private static final int RUNTIME_WATCHDOG_STALE_ROUNDS_BEFORE_RECOVERY = 3; + private static final int RUNTIME_WATCHDOG_STALE_SYNACKS_BEFORE_RECOVERY = 4; + private static final int RUNTIME_WATCHDOG_MAX_STALE_ROUNDS_BEFORE_RECOVERY = 6; private static final int RUNTIME_WATCHDOG_RECOVERY_COOLDOWN_MS = 20000; private static final int RUNTIME_WATCHDOG_HARD_RESTART_COOLDOWN_MS = 60000; private static final int DIAGNOSTIC_WATCHDOG_INTERVAL_MS = 5000; @@ -218,6 +221,9 @@ public class RapVpnService extends VpnService { private volatile String shutdownReason = "service destroyed"; private volatile long lastRuntimeWatchdogRecoveryAt; private volatile long lastRuntimeWatchdogHardRestartAt; + private volatile long lastRuntimeWatchdogDownlinkPackets; + private volatile long lastRuntimeWatchdogUplinkPackets; + private volatile int runtimeWatchdogStaleRounds; private volatile long lastDiagnosticEnsureAt; private volatile long lastDiagnosticStatusEnsureAt; private volatile long lastDiagnosticRestartAt; @@ -1360,6 +1366,11 @@ public class RapVpnService extends VpnService { tcpHandshakeStalls.set(0); runtimeWatchdogHardRestarts.set(0); hardRuntimeRestartInProgress.set(false); + lastRuntimeWatchdogRecoveryAt = 0; + lastRuntimeWatchdogHardRestartAt = 0; + lastRuntimeWatchdogDownlinkPackets = 0; + lastRuntimeWatchdogUplinkPackets = 0; + runtimeWatchdogStaleRounds = 0; downlinkDroppedPackets.set(0); downlinkDroppedBytes.set(0); downlinkTransportChecksumRepairs.set(0); @@ -1539,12 +1550,32 @@ public class RapVpnService extends VpnService { } long now = System.currentTimeMillis(); int stale = staleTCPHandshakeCount(); + long downlinkPackets = downlinkReceivedPackets.get(); + long uplinkPackets = uplinkSentPackets.get(); + boolean downlinkProgressed = downlinkPackets > lastRuntimeWatchdogDownlinkPackets; + boolean uplinkProgressed = uplinkPackets > lastRuntimeWatchdogUplinkPackets; + lastRuntimeWatchdogDownlinkPackets = downlinkPackets; + lastRuntimeWatchdogUplinkPackets = uplinkPackets; if (stale <= 0) { + runtimeWatchdogStaleRounds = 0; + continue; + } + if (downlinkProgressed) { + runtimeWatchdogStaleRounds = 0; + writeRuntimeDetail("watchdog_observed_downlink", "stale=" + stale + " downlink_progress=true uplink_progress=" + uplinkProgressed, "watchdog", runtimeWatchdogRecoveries.get(), tcpHandshakeStalls.get(), "", -1); + continue; + } + runtimeWatchdogStaleRounds++; + if (runtimeWatchdogStaleRounds < RUNTIME_WATCHDOG_STALE_ROUNDS_BEFORE_RECOVERY + || (stale < RUNTIME_WATCHDOG_STALE_SYNACKS_BEFORE_RECOVERY + && runtimeWatchdogStaleRounds < RUNTIME_WATCHDOG_MAX_STALE_ROUNDS_BEFORE_RECOVERY)) { + writeRuntimeDetail("watchdog_waiting", "stale=" + stale + " rounds=" + runtimeWatchdogStaleRounds + " uplink_progress=" + uplinkProgressed, "watchdog", runtimeWatchdogRecoveries.get(), tcpHandshakeStalls.get(), "", -1); continue; } if (now - lastRuntimeWatchdogRecoveryAt < RUNTIME_WATCHDOG_RECOVERY_COOLDOWN_MS) { continue; } + runtimeWatchdogStaleRounds = 0; tcpHandshakeStalls.addAndGet(stale); runtimeWatchdogRecoveries.incrementAndGet(); lastRuntimeWatchdogRecoveryAt = now;