A green VPN icon is a claim about something, and the useful question is what. On a phone it may reflect one client's tunnel, the transport of the current network, or a single successful request to an internal page. Those observations come from different sources, refresh at different rates, and can disagree on the same screen. That is why an icon can look healthy while the browser in front of you fails.

Pin the scope before reading the colour, or the colour will answer a question nobody asked.

The Icon Answers a Narrower Question Than You Think

Start with one declared scope, such as the personal browser using the approved personal tunnel. A work-profile tunnel does not answer that question for a personal app, and a per-app VPN configuration may exclude the very browser you are testing. A local filtering client can show connected while sending nothing to a remote gateway, because its tunnel terminates on the device.

On Android, the network callbacks behind an indicator report transports and capabilities for a network. A VPN transport says which network carries traffic, not which applications or address families use it.

Build the State Table From Evidence

Three or four states are enough if each one names the evidence behind it. The columns below start from what can be observed rather than from the colour you want to show, which keeps a stale reading from becoming a success claim.

Signal actually observedWhat it establishesWhat remains open
Target client reports an established tunnelThat client believes its tunnel is up, within its own scopeOther apps, other profiles, other address families
Active network exposes a VPN transportSome tunnel carries the current networkWhich app it belongs to and where it exits
Probe to a private host returned fresh dataOne request reached one destination at one timeEverything the probe does not touch
Callbacks stopped or data is older than the refresh windowNothing current is knownAny statement about the tunnel state

The last row is the one that gets dropped in practice. An indicator that keeps its final value when its source dies reports history as if it were the present, and users read it as a live state. Label that case stale or unknown, and choose wording that does not imply a tunnel. Then let the label change as soon as the source stops.

Check What the Device Actually Reports

A physical light or a widget should be driven by a supported observation, not by a guess about the last known good state. These Android commands show the transport and the platform's always-on configuration without installing anything.

adb shell dumpsys connectivity | grep -i -A4 vpn
adb shell settings get secure always_on_vpn_app
adb shell settings get secure always_on_vpn_lockdown
adb shell dumpsys netstats | head -40

The always-on keys tell you whether the platform is configured to keep a tunnel up and to block traffic without it; they do not tell you that the tunnel is currently healthy. Pair that configuration reading with a fresh probe before anything is displayed as connected.

If the observation cannot continue, the display must be able to say so. A lamp wired to an automation that no longer runs is worse than no lamp, because it asserts a state nobody is measuring.

Keep Warning Separate From Enforcement

An automation that notices a disconnect fires after the event, so it cannot be the boundary that stops a packet escaping during the gap. If direct fallback must be forbidden, configure the platform's blocking policy and verify it by turning the tunnel off deliberately. The indicator reports; the policy enforces.

A warning task that disables interfaces to force compliance can also lock the device out of the reconnect it needs, producing a loop where the tunnel never returns and the warning never clears.

Decide what the display should show when the observer is unsure, then test that case by stopping the observer. If the answer is still connected, the indicator is measuring the past.

Winndannde jowitiiɗe heen