diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/WatchdogReceiver.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/WatchdogReceiver.java index fe118f4e3..ba8fa1d5f 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/WatchdogReceiver.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/WatchdogReceiver.java @@ -150,6 +150,19 @@ public class WatchdogReceiver extends BroadcastReceiver return Action.RESTART; } + /** + * Whether a check counts towards the steady-state backoff of a live + * process: connected, retrying/connecting, or held back by the ISP + * whitelist. In all of these the in-process machinery owns recovery and + * the alarm can only guard against the process dying - firing every + * minute (and waking Doze every 10 minutes) for hours of failed retries + * or whitelist blocking only burns battery. + */ + static boolean isAliveSteady(Action action, boolean processAlive, boolean parked) + { + return action == Action.NONE && processAlive && !parked; + } + @Override public void onReceive(Context context, Intent intent) { @@ -164,27 +177,24 @@ public class WatchdogReceiver extends BroadcastReceiver return; } reschedule(context); - if (WhiteListManager.getInstance(context).shouldBlockReconnect()) - { /* the ISP whitelist blocks the server; the in-process retry - * machinery parks itself in that case, so should we */ - DiagLog.i(TAG, "whitelist blocks the server, not restarting"); - return; - } + /* the ISP whitelist blocks the server; the in-process retry + * machinery parks itself in that case, so should we */ + boolean whitelistBlocks = WhiteListManager.getInstance(context).shouldBlockReconnect(); VpnStateService service = VpnStateService.getLiveInstance(); boolean processAlive = service != null; boolean serviceTracking = processAlive && service.getState() != VpnStateService.State.DISABLED; boolean wifiPresent = isWifiPresent(context); boolean autoProfileConfigured = AutoConnectManager.getInstance(context).getCurrentConfig().getProfileUuid() != null; - Action action = decideAction(enabled, uuid != null, autoExpected, false, serviceTracking, + Action action = decideAction(enabled, uuid != null, autoExpected, whitelistBlocks, serviceTracking, processAlive, wifiPresent, autoProfileConfigured); - boolean connected = serviceTracking && service.getState() == VpnStateService.State.CONNECTED; boolean parked = uuid == null && autoExpected && processAlive && !serviceTracking && action == Action.NONE; + boolean alive = isAliveSteady(action, processAlive, parked); if (parked) { sParkedChecks++; sConnectedChecks = 0; } - else if (connected && action == Action.NONE) + else if (alive) { sConnectedChecks++; sParkedChecks = 0; @@ -195,8 +205,8 @@ public class WatchdogReceiver extends BroadcastReceiver sConnectedChecks = 0; } boolean parkedSteady = parked && sParkedChecks >= PARKED_BACKOFF_AFTER; - boolean connectedSteady = connected && sConnectedChecks >= CONNECTED_BACKOFF_AFTER; - String cadence = parkedSteady ? "parked" : connectedSteady ? "connected" : null; + boolean connectedSteady = alive && sConnectedChecks >= CONNECTED_BACKOFF_AFTER; + String cadence = parkedSteady ? "parked" : connectedSteady ? "alive" : null; if (cadence != null) { /* steady state: log the transition once, then stay silent - a * per-fire line only floods diag.log and burns flash I/O */ @@ -210,7 +220,7 @@ public class WatchdogReceiver extends BroadcastReceiver { DiagLog.i(TAG, "check: uuid=" + (uuid != null) + " autoExpected=" + autoExpected + " processAlive=" + processAlive + " tracking=" + serviceTracking + - " wifi=" + wifiPresent + " -> " + action); + " whitelist=" + whitelistBlocks + " wifi=" + wifiPresent + " -> " + action); } sLastCadence = cadence; switch (action) diff --git a/src/frontends/android/app/src/test/java/org/strongswan/android/logic/WatchdogReceiverTest.kt b/src/frontends/android/app/src/test/java/org/strongswan/android/logic/WatchdogReceiverTest.kt index d7fd9731c..09f0a1e23 100644 --- a/src/frontends/android/app/src/test/java/org/strongswan/android/logic/WatchdogReceiverTest.kt +++ b/src/frontends/android/app/src/test/java/org/strongswan/android/logic/WatchdogReceiverTest.kt @@ -125,4 +125,28 @@ class WatchdogReceiverTest assertThat(decide(expectedProfileSet = true, autoExpected = true, processAlive = false, wifiPresent = false)) .isEqualTo(Action.RESTART) } + + @Test + fun liveProcessRetryingOrWhitelistedBacksOff() + { + /* retrying/connecting: the service tracks the connection */ + val retrying = decide(expectedProfileSet = true, serviceTracking = true, processAlive = true) + assertThat(retrying).isEqualTo(Action.NONE) + assertThat(WatchdogReceiver.isAliveSteady(retrying, true, false)).isTrue() + /* whitelist blocks the server while the process is alive */ + val blocked = decide(expectedProfileSet = true, whitelistBlocks = true, processAlive = true) + assertThat(WatchdogReceiver.isAliveSteady(blocked, true, false)).isTrue() + } + + @Test + fun deadProcessOrRestartNeverBacksOff() + { + val restart = decide(expectedProfileSet = true, processAlive = false) + assertThat(WatchdogReceiver.isAliveSteady(restart, false, false)).isFalse() + /* whitelist blocks, but no live process: keep the fast cadence */ + val blocked = decide(expectedProfileSet = true, whitelistBlocks = true, processAlive = false) + assertThat(WatchdogReceiver.isAliveSteady(blocked, false, false)).isFalse() + /* parked sessions use their own backoff */ + assertThat(WatchdogReceiver.isAliveSteady(Action.NONE, true, true)).isFalse() + } }