Fix the manager crashing on launch - #802
Merged
Merged
Conversation
OkHttp 5's Android artifact keeps the public suffix list in assets/ and reaches it through a process-static Context that PlatformInitializer sets from androidx.startup. Parasitically the manager's manifest is never installed, so that provider never runs, and DnsOverHttps -- which asks the list whether a host is private before it opens any socket -- died with "Unable to load PublicSuffixDatabase.list" on the very first lookup. Coil was already hand-initialised for this exact reason; OkHttp was not. Initialise it where the one client is built rather than in an activity: that is on the path to every request in both parasitic and standalone runs, and in the debug demo host, which never opens MainActivity. Two things then let a missing asset become a fatal instead of a degraded feature, and both are fixed here: - VectorDns caught only UnknownHostException, so an IllegalStateException escaped it, the latch never closed, and the fallback to the system resolver the class is built around never engaged. - canaryBuilds and frameworkReleases called get() unguarded, unlike every other request in that file. Both are read from a viewModelScope or a LaunchedEffect, and there is no CoroutineExceptionHandler anywhere in the manager, so the throw landed on the main thread. Verified on a Pixel 6: online the store loads with no DoH fallback; offline with the disk cache cleared, DoH falls back once and the release fetch logs "github release list unavailable" instead of taking the process down.
The switch said what DoH would do; nothing said what it was doing. Three outcomes looked identical from the sheet -- the setting off, a proxy taking the decision away, and a lookup that failed and latched the session onto the system resolver -- and the last one existed only as a log line, on the very networks the setting exists for. VectorDns now records what each lookup did and the sheet renders it under the switch. Observed, never probed: a "test DoH" button would be a second code path, another host at another moment, and it can pass while the client that fetches the module list is failing. The fallback state also offers a way back. The latch is what keeps a blocked endpoint from costing five seconds a name, but the session it belongs to is com.android.shell -- a process nobody can restart on purpose -- so one bad lookup on a captive portal disabled DoH until something else killed the host. Two things this turned up, fixed here: - `direct` was a lazy val, so the proxy check ran once per process. Joining a VPN mid-session was invisible and DoH kept querying Cloudflare when the file says it should stand aside. It is read per lookup now, like the setting beside it. - The failure line named the host being resolved, which is whichever request happened to be in flight -- and UnknownHostException carries that name as its entire message, so it printed twice while the useful part, which way it failed, went missing. What failed is reaching Cloudflare, so that is what it says, with the exception type as the detail. Verified on a Pixel 6 in one process: resolved through Cloudflare, forced to fall back with the radios off, recovered by pressing the action, and back to resolving through Cloudflare with no second fallback.
A record outlives the build that wrote it: the directory is this process's cache, which an update does not clear. So after installing a fix the status card still shows the crash that was just fixed, and it stays there until somebody thinks to press clear. That is not a stale line on a screen. It is what a reporter copies into the issue to show the fix did not work -- #799 was answered with five traces from the build before the one that fixed them, which reads as a recurrence and is not one. Matched on the whole build line rather than the hash, because a build from a dirty tree carries the hash of the commit it was built from. Only at install, never at record: this is about what a new build inherits, and a crash loop within one build must keep every record it makes. A file whose second line cannot be read is kept -- losing a trace is the worse of the two failures.
Coil is initialised by hand for the same reason OkHttp now is: parasitically this app's manifest is never installed, so nothing that self-registers there ever runs. But it was done in MainActivity, and the debug demo host never opens MainActivity -- which is the argument that moved OkHttp's initialisation out of the activity in the first place, not followed through for the loader beside it. The demo host therefore had no image loader at all, and every avatar in it failed to load. ServiceLocator.attach is where both entry points already meet. The factory is not called until the first image, so this costs nothing at startup and does not build the OkHttp client before something asks for it.
JingMatrix
force-pushed
the
fix-manager-refactor-regressions
branch
from
July 29, 2026 13:32
6bdcb82 to
70a43cd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
OkHttp 5 ships its public suffix list as an asset and takes the
Contextneeded to read it from anandroidx.startupprovider. The manager's manifest is never installed parasitically, so that provider never runs, and the first DNS lookup threw before opening a socket.HttpClientFactorynow initialises OkHttp explicitly, as Coil already was. This is why installing the APK appeared to work around it: for a normal install the provider does run.Two further defects turned that into a crash rather than a degraded Store, both fixed here:
VectorDnscaught onlyUnknownHostException, so its documented fallback to the system resolver never engaged.frameworkReleasesandcanaryBuildscalled the network unguarded onviewModelScope.Also in this branch:
MainActivitytoServiceLocator.attach, for the same reason OkHttp's lives where the client is built: the debug demo host never opensMainActivity, so it had no image loader at all.The GitHub sign-in removal that was on this branch earlier has been dropped from it, to keep this to the crash and what it turned up. It can come back on its own.
Verified on a Pixel 6: launch from the module action, forced fallback with the radios off, recovery without restarting the process, and a planted crash record from an older build discarded at the next launch while the current build's own record was kept.
Fixes #799.