fix(session): alert on every unsolicited native stop, not just the deadline

Second review pass found that VpnStatus.error is unreachable mid-session:
AppConfigs.V2RAY_STATES has no error state and the plugin's stateName when()
ends in else -> "DISCONNECTED", so Dart's unknown -> error mapping is dead
code on Android. Every native stop therefore arrived as a plain disconnect,
and anything other than the expiry deadline - an Xray or TUN setup failure, a
mid-session fail-closed deadline arm - left the user on their real IP with no
notification and no message. That is the exact hole this feature exists to
close.

stopAll() is the single funnel for every native stop, so the alert moves
there: it fires when the stop interrupted a live tunnel and the user did not
ask for it. isRunning is captured before cleanup() clears it, which keeps a
failed connect silent - it never had a tunnel to interrupt. STOP_SERVICE
(the notification's Disconnect action) and onRevoke pass userRequested: in
the revoke case the user switched VPNs or withdrew consent themselves, and
may well be protected by the app that took over.

Also from the pass:
- re-arming now needs the deadline to move by more than 15 s, so round-trip
  jitter cannot cancel and re-post a warning that is already on screen
- a warning delivered with under a minute left is dropped rather than
  announcing "5 minutes"; the disconnected alert follows anyway
- the _doDisconnect comment no longer claims endedMessage posts the alert
- CHANGELOG: the alert does now cover the tunnel stopping unexpectedly

flutter analyze clean; debug APK builds with the Kotlin recompiled.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
esefxdzandClaude Opus 5 committed 2026-10-17 00:44:51 +03:00
1 parent d1ea13b01a
commit 8ba3b6c543
4 files changed
+43 -24

No files matched your search

+9 -9
View File
@@ -299,10 +299,10 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.20"
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
@@ -315,10 +315,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9"
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
version: "1.19.0"
version: "1.18.0"
nested:
dependency: transitive
description:
@@ -560,10 +560,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
version: "0.7.12"
version: "0.7.11"
typed_data:
dependency: transitive
description:
@@ -648,10 +648,10 @@ packages:
dependency: transitive
description:
name: vector_math
sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.2.0"
vm_service:
dependency: transitive
description:
@@ -725,5 +725,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.11.0-0 <4.0.0"
dart: ">=3.10.3 <4.0.0"
flutter: ">=3.38.4"