Skip to content

fix: reconnect flap and null-body RST (v1.18.1) - #29

Merged
bethropolis merged 3 commits into
mainfrom
dev/next
Sep 16, 2026
Merged

bethropolis merged 3 commits into
mainfrom
dev/next

Conversation

@bethropolis

Copy link
Copy Markdown
Owner

Post-roam stability and wire-protocol hotfixes, all verified live against Android 1.35.13 plus unit/race/integration gates.

fix(reconnect): replace zombie sessions, stop self-redial churn

  • Device.Connect immediately replaces any live session (LanDeviceLink::reset); superseded deaths stay silent
  • Same-IP sightings on live sockets no longer redial (routine broadcasts were churning duplicates the phone RSTs every ~2s)
  • reconnectCooldown 10s -> 1s (upstream Android MILLIS_DELAY = 1000ms)
  • Disconnected sighting dials only roamed (new-IP) targets; backoff covers last-known so post-drop races stop
  • TargetDeviceID mismatch demoted to debug log; aggressive keepalive (30s/10s/3) + 10s write deadline for Send-Q zombies
  • Writer resolves current session per packet; pool packets released on all paths

fix(protocol): never emit body:null

  • Bodyless requests sent explicit "body":null; phone aborts the link on it (RST seconds after every connect carrying the contacts UID request). Nil bodies normalize to {} in NewPacket + both call sites (contacts sync, SMS conversations).

fix(test): hermetic duplicate-session integration test

  • New dedup_test.go (cooldown refusal, replacement, silent superseded close) with per-device event filtering so real-LAN neighbors cannot pollute assertions.

Verified: go vet, go test ./..., go test -tags integration -race, golangci-lint, static binary, live Wi-Fi-toggle auto-reconnect (single silent replace, .177 -> .134).

- Device.Connect immediately replaces any live session (LanDeviceLink::reset):
  old socket closed, superseded readLoop death ignored silently
- Same-IP sightings on live sockets no longer redial; roam repair arrives
  via phone-initiated inbound or IP-change dials, ending the 2s RST war
- reconnectCooldown 10s -> 1s (upstream Android MILLIS_DELAY = 1000ms)
- Disconnected sighting dials only target roamed (new-IP) addresses; the
  backoff loop covers last-known targets so post-drop races stop
- TargetDeviceID mismatch demoted to debug log (stale cached IDs)
- Aggressive TCP keepalive (30s/10s/3) with legacy fallback on both
  dial and accept paths; 10s write deadline kills Send-Q zombies
- Writer resolves current session per packet, releases pool packets on
  both success and error paths
Bodyless requests sent explicit "body":null; at least one phone build
aborts the whole link on it (RST seconds after every connect carrying the
contacts UID request). Normalize nil bodies to {} in NewPacket and at both
existing nil-body call sites (contacts sync, SMS conversations).
Drive the roam race (cooldown refusal, replacement, silent superseded
close) with per-device event filtering so real-LAN neighbors dialing
into :1716 mid-run cannot pollute assertions. Cooldown sleep 11s -> 2s
for the 1s window.
@bethropolis
bethropolis merged commit 10485ed into main Sep 16, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant