feat(core): A.6.1 — use OS connectivity signals to drive reconnect
Extends the A.6 supervisor with an OS-level connectivity hint so a returning network triggers a redial immediately instead of waiting out the current backoff slot (up to 60 s). The watchdog remains the authoritative loss detector — the OS signal is advisory. * `chanora_core::NetworkState` (Unknown / Online / Offline) is owned by `ChanoraSession` via a `tokio::sync::watch::Sender`. `set_network_state()` / `network_state()` are the public accessors. * The supervisor's watch-phase `select!` gains a `network_rx` branch: Offline pre-charges watchdog misses (capped at `MAX_MISSES - 1`) so the next probe failure trips immediately; Online clears stale misses. This shrinks UI-banner latency on a Wi-Fi drop from ~15 s to ~5 s. * The reconnect-loop's backoff sleep races against Online: a transition cuts the sleep short and resets the attempt counter so future losses start at the smallest backoff window again. * `chanora_bridge` adds `BridgeNetworkState` (mirror enum) and a sync `set_network_state(state)` function. On platforms with no signal wired the supervisor stays at Unknown and falls back to pure watchdog/backoff — no behavioural regression vs A.6. * Flutter adds `connectivity_plus ^6.1.0` and wires `_wireConnectivity()` in `main()`: seeds with `checkConnectivity()` then forwards every `onConnectivityChanged` to the bridge, mapping any non-`none` transport to Online. Verified on Moto G Stylus 5G (Android 14): `svc wifi disable && svc data disable` for ~40 s — reconnect banner appeared promptly because the watchdog was pre-charged. After `svc wifi enable && svc data enable` the supervisor woke from its 15 s backoff slot and reconnected within seconds; the channel tree re-rendered without user action.
This commit is contained in:
@@ -233,6 +233,39 @@ pub struct BridgeAudioStats {
|
||||
pub ptt_active: bool,
|
||||
}
|
||||
|
||||
// ---------- Connectivity (A.6.1) ----------
|
||||
|
||||
/// Coarse OS-reported network state. Mirrors
|
||||
/// [`chanora_core::NetworkState`] across the bridge.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BridgeNetworkState {
|
||||
/// No signal seen yet.
|
||||
Unknown,
|
||||
/// OS reports a usable network.
|
||||
Online,
|
||||
/// OS reports no network.
|
||||
Offline,
|
||||
}
|
||||
|
||||
impl From<BridgeNetworkState> for chanora_core::NetworkState {
|
||||
fn from(s: BridgeNetworkState) -> Self {
|
||||
match s {
|
||||
BridgeNetworkState::Unknown => chanora_core::NetworkState::Unknown,
|
||||
BridgeNetworkState::Online => chanora_core::NetworkState::Online,
|
||||
BridgeNetworkState::Offline => chanora_core::NetworkState::Offline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify the core of the latest OS-reported connectivity state.
|
||||
/// Called by the Flutter side from `connectivity_plus` callbacks.
|
||||
/// The core's supervisor uses this to (a) pre-charge the watchdog
|
||||
/// on Offline and (b) short-circuit reconnect backoff on Online.
|
||||
#[frb(sync)]
|
||||
pub fn set_network_state(state: BridgeNetworkState) {
|
||||
session().set_network_state(state.into());
|
||||
}
|
||||
|
||||
// ---------- Events (A.6) ----------
|
||||
|
||||
/// Lifecycle event surfaced to Dart. Schema-controlled mirror of
|
||||
|
||||
Reference in New Issue
Block a user