Stabilize Android VPN watchdog telemetry

This commit is contained in:
2026-05-15 14:43:55 +03:00
parent 94eae6f9f0
commit d43d7b6589
2 changed files with 69 additions and 3 deletions
+2 -2
View File
@@ -30,8 +30,8 @@ android {
applicationId "su.cin.rapvpn" applicationId "su.cin.rapvpn"
minSdk 26 minSdk 26
targetSdk 35 targetSdk 35
versionCode 198 versionCode 199
versionName "0.2.198" versionName "0.2.199"
buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\"" buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\""
buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\"" buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\""
buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\"" buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\""
@@ -81,6 +81,9 @@ public class RapVpnService extends VpnService {
private static final int RUNTIME_WATCHDOG_STALE_ROUNDS_BEFORE_RECOVERY = 3; 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_STALE_SYNACKS_BEFORE_RECOVERY = 4;
private static final int RUNTIME_WATCHDOG_MAX_STALE_ROUNDS_BEFORE_RECOVERY = 6; private static final int RUNTIME_WATCHDOG_MAX_STALE_ROUNDS_BEFORE_RECOVERY = 6;
private static final int RUNTIME_WATCHDOG_OPEN_RELAY_STALE_ROUNDS_BEFORE_RECOVERY = 15;
private static final int RUNTIME_WATCHDOG_OPEN_RELAY_STALE_SYNACKS_BEFORE_RECOVERY = 12;
private static final int RUNTIME_WATCHDOG_RECENT_DOWNLINK_GRACE_MS = 30000;
private static final int RUNTIME_WATCHDOG_RECOVERY_COOLDOWN_MS = 20000; 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 RUNTIME_WATCHDOG_HARD_RESTART_COOLDOWN_MS = 60000;
private static final int DIAGNOSTIC_WATCHDOG_INTERVAL_MS = 5000; private static final int DIAGNOSTIC_WATCHDOG_INTERVAL_MS = 5000;
@@ -222,6 +225,7 @@ public class RapVpnService extends VpnService {
private volatile long lastRuntimeWatchdogHardRestartAt; private volatile long lastRuntimeWatchdogHardRestartAt;
private volatile long lastRuntimeWatchdogDownlinkPackets; private volatile long lastRuntimeWatchdogDownlinkPackets;
private volatile long lastRuntimeWatchdogUplinkPackets; private volatile long lastRuntimeWatchdogUplinkPackets;
private volatile long lastRuntimeWatchdogDownlinkProgressAt;
private volatile int runtimeWatchdogStaleRounds; private volatile int runtimeWatchdogStaleRounds;
private volatile long lastDiagnosticEnsureAt; private volatile long lastDiagnosticEnsureAt;
private volatile long lastDiagnosticStatusEnsureAt; private volatile long lastDiagnosticStatusEnsureAt;
@@ -1369,6 +1373,7 @@ public class RapVpnService extends VpnService {
lastRuntimeWatchdogHardRestartAt = 0; lastRuntimeWatchdogHardRestartAt = 0;
lastRuntimeWatchdogDownlinkPackets = 0; lastRuntimeWatchdogDownlinkPackets = 0;
lastRuntimeWatchdogUplinkPackets = 0; lastRuntimeWatchdogUplinkPackets = 0;
lastRuntimeWatchdogDownlinkProgressAt = runtimeStartedAt;
runtimeWatchdogStaleRounds = 0; runtimeWatchdogStaleRounds = 0;
downlinkDroppedPackets.set(0); downlinkDroppedPackets.set(0);
downlinkDroppedBytes.set(0); downlinkDroppedBytes.set(0);
@@ -1394,7 +1399,9 @@ public class RapVpnService extends VpnService {
uplinkReadPps = 0f; uplinkReadPps = 0f;
uplinkSentPps = 0f; uplinkSentPps = 0f;
downlinkReceivedPps = 0f; downlinkReceivedPps = 0f;
getSharedPreferences(PREFS, MODE_PRIVATE).edit() SharedPreferences.Editor editor = getSharedPreferences(PREFS, MODE_PRIVATE).edit();
clearRuntimeDiagnosticPrefs(editor);
editor
.putString("state", "resetting") .putString("state", "resetting")
.putString("message", "runtime counters reset") .putString("message", "runtime counters reset")
.putLong("updated_at", runtimeStartedAt) .putLong("updated_at", runtimeStartedAt)
@@ -1429,6 +1436,7 @@ public class RapVpnService extends VpnService {
.putLong("local_dns_errors", 0) .putLong("local_dns_errors", 0)
.putLong("runtime_watchdog_recoveries", 0) .putLong("runtime_watchdog_recoveries", 0)
.putLong("tcp_handshake_stalls", 0) .putLong("tcp_handshake_stalls", 0)
.putLong("runtime_watchdog_hard_restarts", 0)
.putInt("uplink_worker_count", 0) .putInt("uplink_worker_count", 0)
.putString("uplink_queue_depths", "") .putString("uplink_queue_depths", "")
.putInt("uplink_queue_depth_max", 0) .putInt("uplink_queue_depth_max", 0)
@@ -1443,6 +1451,45 @@ public class RapVpnService extends VpnService {
.apply(); .apply();
} }
private void clearRuntimeDiagnosticPrefs(SharedPreferences.Editor editor) {
String[] prefixes = new String[]{
"uplink",
"uplink_sender",
"uplink_tcp",
"downlink",
"downlink_tcp",
"downlink_writer",
"relay",
"watchdog"
};
String[] suffixes = new String[]{
"state",
"message",
"updated_at",
"thread_alive",
"packets",
"errors",
"bytes",
"rate_mbps",
"rate_pps",
"error_type"
};
for (String prefix : prefixes) {
for (String suffix : suffixes) {
editor.remove(prefix + "_" + suffix);
}
}
for (int i = 0; i < 8; i++) {
editor.remove("uplink_queue_" + i + "_offers");
editor.remove("uplink_queue_" + i + "_drops");
editor.remove("uplink_sender_worker_packets_" + i);
editor.remove("uplink_sender_worker_errors_" + i);
editor.remove("downlink_queue_" + i + "_offers");
editor.remove("downlink_queue_" + i + "_drops");
editor.remove("downlink_writer_flow_packets_" + i);
}
}
private static AtomicLong[] createAtomicCounters(int count) { private static AtomicLong[] createAtomicCounters(int count) {
AtomicLong[] values = new AtomicLong[count]; AtomicLong[] values = new AtomicLong[count];
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@@ -1555,6 +1602,9 @@ public class RapVpnService extends VpnService {
boolean uplinkProgressed = uplinkPackets > lastRuntimeWatchdogUplinkPackets; boolean uplinkProgressed = uplinkPackets > lastRuntimeWatchdogUplinkPackets;
lastRuntimeWatchdogDownlinkPackets = downlinkPackets; lastRuntimeWatchdogDownlinkPackets = downlinkPackets;
lastRuntimeWatchdogUplinkPackets = uplinkPackets; lastRuntimeWatchdogUplinkPackets = uplinkPackets;
if (downlinkProgressed) {
lastRuntimeWatchdogDownlinkProgressAt = now;
}
if (stale <= 0) { if (stale <= 0) {
runtimeWatchdogStaleRounds = 0; runtimeWatchdogStaleRounds = 0;
continue; continue;
@@ -1565,6 +1615,17 @@ public class RapVpnService extends VpnService {
continue; continue;
} }
runtimeWatchdogStaleRounds++; runtimeWatchdogStaleRounds++;
boolean relayOpen = isPacketWebSocketRelayOpen();
boolean recentDownlink = lastRuntimeWatchdogDownlinkProgressAt > 0
&& now - lastRuntimeWatchdogDownlinkProgressAt < RUNTIME_WATCHDOG_RECENT_DOWNLINK_GRACE_MS;
boolean recentUplinkSendError = lastUplinkSendErrorMessage != null && !lastUplinkSendErrorMessage.isEmpty();
if (relayOpen && !recentUplinkSendError
&& (recentDownlink
|| stale < RUNTIME_WATCHDOG_OPEN_RELAY_STALE_SYNACKS_BEFORE_RECOVERY
|| runtimeWatchdogStaleRounds < RUNTIME_WATCHDOG_OPEN_RELAY_STALE_ROUNDS_BEFORE_RECOVERY)) {
writeRuntimeDetail("watchdog_open_relay_waiting", "stale=" + stale + " rounds=" + runtimeWatchdogStaleRounds + " relay_open=true recent_downlink=" + recentDownlink + " uplink_progress=" + uplinkProgressed, "watchdog", runtimeWatchdogRecoveries.get(), tcpHandshakeStalls.get(), "", -1);
continue;
}
if (runtimeWatchdogStaleRounds < RUNTIME_WATCHDOG_STALE_ROUNDS_BEFORE_RECOVERY if (runtimeWatchdogStaleRounds < RUNTIME_WATCHDOG_STALE_ROUNDS_BEFORE_RECOVERY
|| (stale < RUNTIME_WATCHDOG_STALE_SYNACKS_BEFORE_RECOVERY || (stale < RUNTIME_WATCHDOG_STALE_SYNACKS_BEFORE_RECOVERY
&& runtimeWatchdogStaleRounds < RUNTIME_WATCHDOG_MAX_STALE_ROUNDS_BEFORE_RECOVERY)) { && runtimeWatchdogStaleRounds < RUNTIME_WATCHDOG_MAX_STALE_ROUNDS_BEFORE_RECOVERY)) {
@@ -1593,6 +1654,11 @@ public class RapVpnService extends VpnService {
} }
} }
private boolean isPacketWebSocketRelayOpen() {
VpnPacketWebSocketRelay relay = packetWebSocketRelay;
return relay != null && relay.isOpen();
}
private void runDiagnosticServiceWatchdog() { private void runDiagnosticServiceWatchdog() {
getSharedPreferences(PREFS, MODE_PRIVATE).edit() getSharedPreferences(PREFS, MODE_PRIVATE).edit()
.putLong("diagnostic_watchdog_started_at", System.currentTimeMillis()) .putLong("diagnostic_watchdog_started_at", System.currentTimeMillis())