From 72de26d732030d998a0be8739851b763c918a2ca Mon Sep 17 00:00:00 2001 From: Denozordec Date: Fri, 13 Nov 2026 22:41:14 +0700 Subject: [PATCH] =?UTF-8?q?android:=20the=20diagnostics=20row=20=C2=ABRuss?= =?UTF-8?q?ian=20sites=C2=BB=20no=20longer=20shows=20a=20stale=20verdict:?= =?UTF-8?q?=20the=20screen=20waited=20a=20fixed=202.5=20s=20for=20the=20di?= =?UTF-8?q?rect=20probe=20round=20and=20then=20read=20whatever=20the=20las?= =?UTF-8?q?t=20round=20had=20left,=20and=20a=20round=20with=20a=20timed-ou?= =?UTF-8?q?t=20foreign=20probe=20takes=206.7=20s=20or=20more,=20so=20a=20?= =?UTF-8?q?=C2=ABdo=20not=20open=C2=BB=20recorded=20during=20a=20network?= =?UTF-8?q?=20outage=20was=20shown=20minutes=20later=20on=20a=20working=20?= =?UTF-8?q?network;=20it=20now=20waits=20for=20a=20round=20that=20finished?= =?UTF-8?q?=20after=20the=20request=20(up=20to=208=20s)=20and=20shows=20?= =?UTF-8?q?=C2=ABunknown=C2=BB=20otherwise;=20the=20=C2=ABDirect=20round?= =?UTF-8?q?=C2=BB=20line=20goes=20to=20diag.log;=20the=20report=20gives=20?= =?UTF-8?q?its=20checks=2015=20s=20instead=20of=2010?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.2 --- .../logic/connectivity/WhiteListManager.kt | 27 +++++++++++++++++-- .../org/strongswan/android/ui/ReportSheet.kt | 4 +-- .../android/ui/compose/DiagnosticsScreen.kt | 9 ++++--- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/connectivity/WhiteListManager.kt b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/connectivity/WhiteListManager.kt index 32ceaad52..2bc90c348 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/connectivity/WhiteListManager.kt +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/connectivity/WhiteListManager.kt @@ -38,11 +38,14 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.withTimeoutOrNull import org.json.JSONArray import org.json.JSONException import org.json.JSONObject +import org.strongswan.android.logic.DiagLog import org.strongswan.android.logic.VpnStateService import org.strongswan.android.logic.events.Events import java.util.concurrent.ConcurrentHashMap @@ -351,6 +354,23 @@ class WhiteListManager private constructor(private val context: Context) : VpnSt scope.launch { runCheck(force = true) } } + /** + * Runs a probe round and waits for its result: the state of a round that + * finished after this call, or null when none did within [timeoutMs] + * (checks disabled, or the round is slower). A round already in flight + * counts, so a busy cycle does not cost the whole timeout for nothing. + */ + suspend fun checkFresh(timeoutMs: Long): WhiteListState? + { + if (!running || !config.enabled) + { + return null + } + val before = _state.value.lastCheckMs + checkNow() + return withTimeoutOrNull(timeoutMs) { _state.first { it.lastCheckMs != before } } + } + override fun stateChanged() { handler.post { onStateChanged() } @@ -434,8 +454,11 @@ class WhiteListManager private constructor(private val context: Context) : VpnSt val foreignLog = round.foreignOks.mapIndexed { index, ok -> "${targets.getOrNull(index)?.host ?: index}=${ok ?: "?"}/${round.foreignRttMs.getOrNull(index) ?: "-"}ms" } - Log.i(TAG, "Direct round: ${foreignLog.joinToString(" ")}, " + - "domestic=${round.domesticOk}/${round.domesticRttMs ?: "-"}ms -> $candidate") + val roundLog = "Direct round: ${foreignLog.joinToString(" ")}, " + + "domestic=${round.domesticOk}/${round.domesticRttMs ?: "-"}ms -> $candidate" + Log.i(TAG, roundLog) + /* also in the report: tells a stale verdict from a real failure */ + DiagLog.i(TAG, roundLog) val before = machine machine = before.advance(candidate) publish(round) diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ReportSheet.kt b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ReportSheet.kt index b4e36a6b6..eef3c5e9b 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ReportSheet.kt +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ReportSheet.kt @@ -128,8 +128,8 @@ class ReportViewModel(app: Application) : AndroidViewModel(app) val note = comment val diag = attachDiag job = viewModelScope.launch { - /* the same checks as the Diagnostics screen (~3 s); a hang must not block the report */ - val checks = if (!diag) null else withTimeoutOrNull(10_000) { + /* the same checks as the Diagnostics screen (1-8 s); a hang must not block the report */ + val checks = if (!diag) null else withTimeoutOrNull(15_000) { runCatching { runDiagnostics(app) }.getOrNull()?.steps?.map { ReportContent.Check(it.title, it.value, it.ok) } } val result = withContext(Dispatchers.IO) { diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/DiagnosticsScreen.kt b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/DiagnosticsScreen.kt index 9a4564c52..b882fa3c2 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/DiagnosticsScreen.kt +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/DiagnosticsScreen.kt @@ -97,13 +97,14 @@ internal suspend fun runDiagnostics(context: Context): DiagReport = coroutineSco val vpnUp = vpn?.state == VpnStateService.State.CONNECTED val gateway = (vpn?.profile)?.gateway val whiteList = WhiteListManager.getInstance(context) - whiteList.checkNow() val server = async { if (gateway != null) ConnectivityProbe.probe(ProbeTarget.SERVER, 3000, context, gateway) else null } val foreign = async { ConnectivityProbe.probe(ProbeTarget.GOOGLE, 3000, context, null) } val network = withContext(Dispatchers.Default) { networkLabel(context) } - /* the whitelist check runs its own direct round; give it a moment */ - delay(2500) - val wl = whiteList.state.value + /* the whitelist check runs its own direct round: wait for that very round, a + * verdict left from an earlier one (say, a network outage) is not shown. + * 8 s covers a round whose foreign probes time out and are retried. */ + val fresh = whiteList.checkFresh(8000) + val wl = fresh ?: whiteList.state.value.copy(domesticDirectOk = null, domesticDirectRttMs = null) val serverResult = server.await() val foreignResult = foreign.await() val steps = listOf(