Commit Graph

2 Commits

Author SHA1 Message Date
Zoltán Papp
408301714e [client] Fix two concurrency bugs in the Android session binding
Status() read the run loop's label outside any synchronization and then
stored it, so a login or extend completing in that window was undone by
the stale observation, stranding the UI on "login required" over a
healthy session. Guard the latch with a mutex and a generation counter,
and route both clears through the same helper, so a clear that lands
mid-observation wins.

The watch goroutines kept delivering to removed and replaced listeners:
both subscriptions are buffered (one pending tick, ten pending events),
and unsubscribing only stops new items while the loops drain what is
already queued. On Android those callbacks cross into Java, so a
delivery after teardown can reach a listener whose collaborators are
gone. Give each registration a done signal, close it before
unsubscribing, and check it immediately before every callback.
2026-07-28 17:17:43 +02:00
Zoltán Papp
d00068bd89 [client] Expose the SSO auth session to the Android binding
The Android client had no way to see or refresh the peer's SSO session:
the core tracked the deadline and published expiry warnings, but none of
it was exported, so an expired session surfaced only as a raw error
string from the engine run loop.

Mirror the surface the daemon serves its tray:

- Status() and SessionExpiresAtUnix() report the run-loop status label
  and the tracked deadline, the two values StatusResponse carries.
- StateChangeListener signals state changes (payload-free, consumers
  re-read the getters) and forwards the session-expiry warnings from
  the engine's watcher, filtered out of the shared event stream.
- ExtendAuthSession() runs the interactive SSO flow and refreshes the
  deadline without touching the tunnel, with CancelExtendAuthSession()
  for an abandoned browser round-trip — its PKCE wait would otherwise
  hold the loopback port until it timed out and block every retry.
- DismissSessionWarning() suppresses the final warning.

Status() latches NeedsLogin: the run loop keeps its status in a
per-run context state that a restart replaces with a fresh Idle one, so
an engine restart would otherwise erase the fact that the peer still
needs to log in. Only a successful login or extend clears it.
2026-07-28 16:47:53 +02:00