Skip to content

Compare search domains and excluded routes in Builder.equals - #965

Merged
kasnder merged 1 commit into
masterfrom
claude/adoring-wozniak-1vtefl
Sep 19, 2026
Merged

kasnder merged 1 commit into
masterfrom
claude/adoring-wozniak-1vtefl

Conversation

@kasnder

@kasnder kasnder commented Sep 19, 2026

Copy link
Copy Markdown
Member

Closes the rest of #763, after #960 took the metered half.

The bug

Builder.equals decides whether reload() replaces the live tun or takes the "Native restart" shortcut, so any field the builder does not capture cannot reach a running interface. Two were still missing:

  • search domains (addSearchDomain);
  • the carrier ePDG exclusions (excludeRoute, API 33+). These are re-resolved on every rebuild behind a 1.5s timeout, so a first establish whose lookup timed out was never replaced by a later rebuild that did resolve them — Wi-Fi calling stayed broken until some unrelated change forced a real replacement. This is the user-visible half of what is left.

The change

  • Builder overrides addSearchDomain and excludeRoute to record what was added, and equals compares both lists the same way it compares addresses, routes, DNS and disallowed apps.
  • Two offline builders (both networkInfo null) now compare equal instead of forcing a needless interface replacement on every reload while there is no active network at all.
  • The (Builder) obj cast is guarded by an instanceof check rather than by a null check that followed it.

The original triage asked for this to be sequenced behind the replacement-race work, since the fix deliberately produces more interface replacements. VpnReplacementSequencer is now in master, so the replacements go through it.

Testing

Not compiled locally: this container has no Android SDK. Relying on CI for compileGithubDebugJavaWithJavac, the unit tests and lint. Builder is a private inner class of ServiceSinkhole with no seam for a unit test, so there is no test to add here.

On-device confirmation worth having, on a device with a SIM whose ePDG domain resolves: with Wi-Fi calling active, force a VPN rebuild (network switch) after a start where the ePDG lookup timed out, and check the log shows an interface replacement rather than "Native restart".

https://claude.ai/code/session_016eKiHiWiQwMQosDXj8qY4s


Generated by Claude Code

Builder.equals decides whether reload() replaces the live tun or takes
the "Native restart" shortcut, so any field it does not capture cannot
reach a running interface. Two were still missing after #960 fixed the
metered flag:

- search domains (addSearchDomain);
- the carrier ePDG exclusions (excludeRoute, API 33+). These are
  re-resolved on every rebuild behind a 1.5s timeout, so a first
  establish whose lookup timed out was never replaced by a later rebuild
  that resolved them, and Wi-Fi calling stayed broken until some
  unrelated change forced a real replacement.

Both are now recorded by overriding the corresponding Builder methods
and compared like the existing lists.

Also: two offline builders (both networkInfo null) now compare equal
instead of forcing a needless replacement on every reload while there is
no active network, and the cast to Builder is guarded by an instanceof
check rather than relying on a following null check.

The extra interface replacements this produces go through
VpnReplacementSequencer, which was not in place when the issue was first
triaged.

Refs #763

Claude-Session: https://claude.ai/code/session_016eKiHiWiQwMQosDXj8qY4s
@kasnder
kasnder marked this pull request as ready for review September 19, 2026 20:14
@kasnder
kasnder merged commit 4d24c62 into master Sep 19, 2026
3 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.

2 participants