Guard Android VPN runtime generations
This commit is contained in:
@@ -30,8 +30,8 @@ android {
|
||||
applicationId "su.cin.rapvpn"
|
||||
minSdk 26
|
||||
targetSdk 35
|
||||
versionCode 200
|
||||
versionName "0.2.200"
|
||||
versionCode 201
|
||||
versionName "0.2.201"
|
||||
buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\""
|
||||
buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\""
|
||||
buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\""
|
||||
|
||||
@@ -195,6 +195,7 @@ public class RapVpnService extends VpnService {
|
||||
private final AtomicLong runtimeWatchdogRecoveries = new AtomicLong();
|
||||
private final AtomicLong tcpHandshakeStalls = new AtomicLong();
|
||||
private final AtomicLong runtimeWatchdogHardRestarts = new AtomicLong();
|
||||
private final AtomicLong runtimeGeneration = new AtomicLong();
|
||||
private final AtomicLong diagnosticEnsureRequests = new AtomicLong();
|
||||
private final AtomicLong diagnosticRestartRequests = new AtomicLong();
|
||||
private final AtomicBoolean hardRuntimeRestartInProgress = new AtomicBoolean();
|
||||
@@ -1025,6 +1026,7 @@ public class RapVpnService extends VpnService {
|
||||
writeActivePacketRelayConfig(selectedRelayUrl, relayUrls);
|
||||
stopPacketRelay();
|
||||
running = true;
|
||||
long runtimeId = runtimeGeneration.incrementAndGet();
|
||||
runtimeStartedAt = System.currentTimeMillis();
|
||||
uplinkWorkerCount = Math.max(1, Math.min(UPLINK_WORKER_MAX_COUNT, Math.max(1, Runtime.getRuntime().availableProcessors() - 1)));
|
||||
if (uplinkWorkerCount < 2) {
|
||||
@@ -1073,15 +1075,15 @@ public class RapVpnService extends VpnService {
|
||||
writeRuntimeStatus("relay_reset_warning", "queue reset failed: " + e.getMessage(), 0, 0, 0, 1);
|
||||
}
|
||||
}, "rap-vpn-relay-reset");
|
||||
uplinkThread = new Thread(() -> pumpTunToRelay(clusterId, vpnConnectionId), "rap-vpn-uplink");
|
||||
uplinkThread = new Thread(() -> pumpTunToRelay(runtimeId, clusterId, vpnConnectionId), "rap-vpn-uplink");
|
||||
uplinkSenderThreads = new Thread[uplinkWorkerCount];
|
||||
for (int i = 0; i < uplinkWorkerCount; i++) {
|
||||
final int workerIndex = i;
|
||||
uplinkSenderThreads[i] = new Thread(() -> pumpUplinkQueueToRelay(workerIndex, clusterId, vpnConnectionId), "rap-vpn-uplink-sender-" + workerIndex);
|
||||
uplinkSenderThreads[i] = new Thread(() -> pumpUplinkQueueToRelay(runtimeId, workerIndex, clusterId, vpnConnectionId), "rap-vpn-uplink-sender-" + workerIndex);
|
||||
}
|
||||
downlinkThread = new Thread(() -> runDownlinkWithRestart(clusterId, vpnConnectionId), "rap-vpn-downlink-receiver");
|
||||
downlinkWriterThread = new Thread(this::pumpDownlinkQueueToTun, "rap-vpn-downlink-writer");
|
||||
runtimeWatchdogThread = new Thread(() -> runRuntimeWatchdog(clusterId, vpnConnectionId), "rap-vpn-runtime-watchdog");
|
||||
downlinkThread = new Thread(() -> runDownlinkWithRestart(runtimeId, clusterId, vpnConnectionId), "rap-vpn-downlink-receiver");
|
||||
downlinkWriterThread = new Thread(() -> pumpDownlinkQueueToTun(runtimeId), "rap-vpn-downlink-writer");
|
||||
runtimeWatchdogThread = new Thread(() -> runRuntimeWatchdog(runtimeId, clusterId, vpnConnectionId), "rap-vpn-runtime-watchdog");
|
||||
diagnosticWatchdogThread = new Thread(this::runDiagnosticServiceWatchdog, "rap-vpn-diagnostic-watchdog");
|
||||
if (resetThread != null) {
|
||||
resetThread.start();
|
||||
@@ -1308,6 +1310,7 @@ public class RapVpnService extends VpnService {
|
||||
}
|
||||
|
||||
private void stopPacketRelay() {
|
||||
runtimeGeneration.incrementAndGet();
|
||||
running = false;
|
||||
VpnPacketWebSocketRelay relay = packetWebSocketRelay;
|
||||
packetWebSocketRelay = null;
|
||||
@@ -1564,13 +1567,17 @@ public class RapVpnService extends VpnService {
|
||||
}
|
||||
}
|
||||
|
||||
private void runDownlinkWithRestart(String clusterId, String vpnConnectionId) {
|
||||
private boolean isRuntimeActive(long runtimeId) {
|
||||
return running && runtimeGeneration.get() == runtimeId;
|
||||
}
|
||||
|
||||
private void runDownlinkWithRestart(long runtimeId, String clusterId, String vpnConnectionId) {
|
||||
long restarts = 0;
|
||||
while (running) {
|
||||
while (isRuntimeActive(runtimeId)) {
|
||||
downlinkRestarts = restarts;
|
||||
writeRuntimeDetail("starting", "downlink loop starting", "downlink", 0, restarts, "");
|
||||
pumpRelayToTun(clusterId, vpnConnectionId);
|
||||
if (!running) {
|
||||
if (!isRuntimeActive(runtimeId)) {
|
||||
return;
|
||||
}
|
||||
restarts++;
|
||||
@@ -1580,18 +1587,18 @@ public class RapVpnService extends VpnService {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
if (!running) {
|
||||
if (!isRuntimeActive(runtimeId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runRuntimeWatchdog(String clusterId, String vpnConnectionId) {
|
||||
while (running) {
|
||||
private void runRuntimeWatchdog(long runtimeId, String clusterId, String vpnConnectionId) {
|
||||
while (isRuntimeActive(runtimeId)) {
|
||||
try {
|
||||
Thread.sleep(RUNTIME_WATCHDOG_INTERVAL_MS);
|
||||
if (!running) {
|
||||
if (!isRuntimeActive(runtimeId)) {
|
||||
return;
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
@@ -1645,7 +1652,7 @@ public class RapVpnService extends VpnService {
|
||||
recoverPacketRelayRuntime(clusterId, vpnConnectionId, "tcp_handshake_stall stale=" + stale);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
if (!running) {
|
||||
if (!isRuntimeActive(runtimeId)) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -1821,7 +1828,7 @@ public class RapVpnService extends VpnService {
|
||||
return value == null || value.isEmpty() ? "missing" : "present";
|
||||
}
|
||||
|
||||
private void pumpTunToRelay(String clusterId, String vpnConnectionId) {
|
||||
private void pumpTunToRelay(long runtimeId, String clusterId, String vpnConnectionId) {
|
||||
byte[] packet = new byte[32767];
|
||||
long readPackets = 0;
|
||||
FileDescriptor fd = null;
|
||||
@@ -1831,7 +1838,7 @@ public class RapVpnService extends VpnService {
|
||||
input = new FileInputStream(fd);
|
||||
uplinkTunFd = fd;
|
||||
uplinkTunInput = input;
|
||||
while (running) {
|
||||
while (isRuntimeActive(runtimeId)) {
|
||||
int n = input.read(packet);
|
||||
if (n > 0) {
|
||||
readPackets++;
|
||||
@@ -1849,7 +1856,7 @@ public class RapVpnService extends VpnService {
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (running) {
|
||||
if (isRuntimeActive(runtimeId)) {
|
||||
Log.e(TAG, "vpn uplink stopped", e);
|
||||
writeRuntimeStatus("error", "uplink stopped: " + e.getMessage(), readPackets, 0, 0, 0);
|
||||
writeRuntimeDetail("stopped", "uplink stopped: " + e.getMessage(), "uplink", readPackets, 0, e.getClass().getSimpleName());
|
||||
@@ -2474,11 +2481,11 @@ public class RapVpnService extends VpnService {
|
||||
}
|
||||
}
|
||||
|
||||
private void pumpUplinkQueueToRelay(int workerIndex, String clusterId, String vpnConnectionId) {
|
||||
private void pumpUplinkQueueToRelay(long runtimeId, int workerIndex, String clusterId, String vpnConnectionId) {
|
||||
long sentPackets = 0;
|
||||
long errors = 0;
|
||||
List<byte[]> batch = new ArrayList<>(VPN_BATCH_MAX_PACKETS);
|
||||
while (running) {
|
||||
while (isRuntimeActive(runtimeId)) {
|
||||
try {
|
||||
BlockingQueue<byte[]>[] queues = uplinkQueues;
|
||||
if (queues == null || workerIndex < 0 || workerIndex >= queues.length) {
|
||||
@@ -2556,12 +2563,12 @@ public class RapVpnService extends VpnService {
|
||||
writeRuntimeStatus("uplink_sent", "sent batch=" + batch.size(), 0, sentPackets, 0, errors);
|
||||
writeRuntimeDetail("sent", "worker=" + workerIndex + " sent batch=" + batch.size(), "uplink_sender", sentPackets, errors, "", workerIndex);
|
||||
} catch (InterruptedException e) {
|
||||
if (!running) {
|
||||
if (!isRuntimeActive(runtimeId)) {
|
||||
return;
|
||||
}
|
||||
writeRuntimeDetail("read_wait", "uplink queue wait interrupted", "uplink_sender", sentPackets, errors, e.getClass().getSimpleName(), workerIndex);
|
||||
} catch (Exception e) {
|
||||
if (running) {
|
||||
if (isRuntimeActive(runtimeId)) {
|
||||
Log.w(TAG, "vpn uplink batch send failed; continuing", e);
|
||||
errors++;
|
||||
AtomicLong[] senderErrors = uplinkSenderErrorsByWorker;
|
||||
@@ -2573,7 +2580,7 @@ public class RapVpnService extends VpnService {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException interrupted) {
|
||||
if (!running) {
|
||||
if (!isRuntimeActive(runtimeId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -3191,7 +3198,7 @@ public class RapVpnService extends VpnService {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void pumpDownlinkQueueToTun() {
|
||||
private void pumpDownlinkQueueToTun(long runtimeId) {
|
||||
long writtenPackets = 0;
|
||||
long errors = 0;
|
||||
FileDescriptor fd = null;
|
||||
@@ -3199,7 +3206,7 @@ public class RapVpnService extends VpnService {
|
||||
try {
|
||||
fd = Os.dup(tunnel.getFileDescriptor());
|
||||
downlinkTunFd = fd;
|
||||
while (running) {
|
||||
while (isRuntimeActive(runtimeId)) {
|
||||
BlockingQueue<byte[]>[] queues = downlinkQueues;
|
||||
if (queues == null || queues.length == 0) {
|
||||
return;
|
||||
@@ -3228,7 +3235,7 @@ public class RapVpnService extends VpnService {
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (running) {
|
||||
if (isRuntimeActive(runtimeId)) {
|
||||
Log.e(TAG, "vpn downlink writer stopped", e);
|
||||
writeRuntimeStatus("error", "downlink writer stopped: " + e.getMessage(), 0, 0, writtenPackets, errors);
|
||||
writeRuntimeDetail("stopped", "downlink writer stopped: " + e.getMessage(), "downlink_writer", writtenPackets, errors, e.getClass().getSimpleName());
|
||||
|
||||
Reference in New Issue
Block a user