fix(android): Treat an unpopulated connection cache as stale (JAVA-717) - #6029
Draft
runningcode wants to merge 3 commits into
Draft
fix(android): Treat an unpopulated connection cache as stale (JAVA-717)#6029runningcode wants to merge 3 commits into
runningcode wants to merge 3 commits into
Conversation
`When network is active but not connected with permission, return DISCONNECTED` mocked an active network reporting isConnected=false alongside NetworkCapabilities describing a validated WiFi link. Those describe opposite worlds. It passes today only because the empty connection cache reads as fresh for the first two minutes of every boot (JAVA-717), which forces the legacy activeNetworkInfo path where the capability mocks are never consulted. buildInfo reports API 24, so once that bug is fixed the provider reads capabilities and the test would fail for a reason that has nothing to do with what it is named after. Fixing the mocks first keeps that failure from being buried in the commit that fixes the cache. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
lastCacheUpdateTime used 0 for "never populated" while being compared against SystemClock.uptimeMillis(), which starts at 0 at boot. For the first two minutes of every boot the empty cache therefore read as fresh, so getConnectionStatus() skipped updateCache() and fell through to the legacy activeNetworkInfo path instead of reading NetworkCapabilities. The window reopens after every unregisterNetworkCallback(), which reset the field to 0. Switching clocks does not fix this on its own: elapsedRealtimeNanos() also starts at 0 at boot. Any 0-means-unset long compared against a boot-relative clock has the same flaw; only epoch millis made it safe, because there 0 is 1970. The cache now holds a Deadline, so "never populated" is expired by construction and has no numeric value to get wrong. The provider takes an ElapsedRealtimeClock in place of ICurrentDateProvider, which is what a two-minute TTL wants: it must keep counting while the device sleeps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📲 Install BuildsAndroid
|
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.
📜 Description
AndroidConnectionStatusProviderused0to mean "cache never populated", and compared it againstSystemClock.uptimeMillis():uptimeMillis()is 0 at boot, so0does not mean "unset" — it means "populated at boot". For the first two minutes of every boot, an empty cache reads as fresh.The cache now holds a
Deadline, so "never populated" is expired by construction and has no numeric value to get wrong.Stacked on #6028, which adds the clock types this uses.
💡 Motivation and Context
getConnectionStatus()andgetConnectionType()both callupdateCache(null)only whenisCacheValid()is false. During that two-minute window they skip it and fall through togetConnectionStatusFromCache(), which findscachedNetworkCapabilities == nulland answers from the legacyactiveNetworkInfopath instead ofNetworkCapabilities. So the answer is not wrong, but the cache is silently doing nothing during exactly the window where an app is most likely to be starting up — andunregisterNetworkCallback()reset the field to0, reopening the window every time.Changing clocks does not fix it:
elapsedRealtimeNanos()starts at 0 at boot too. Any0-means-unset long compared against a boot-relative clock has this flaw. Only epoch millis made it safe, because there0is 1970 — which is why the bug predates the clock work and is not caused by it.The provider now takes an
ElapsedRealtimeClockin place ofICurrentDateProvider. That is the right guarantee for a TTL: a cache entry should go stale on a real-time schedule whether or not the device was awake.💚 How did you test it?
A regression test asserts that a provider constructed at tick 0 populates the cache before reading it. It fails if the deadline is constructed fresh instead of passed, which is the shape of the original bug.
The test file moves from a mocked
ICurrentDateProvidertoTestTicker, so the TTL tests advance by an amount and a unit rather than by a bare+= 60 * 1000L.One existing test changed.
When network is active but not connected with permission, return DISCONNECTEDmocked an active network reportingisConnected=falsealongsideNetworkCapabilitiesdescribing a validated WiFi link — opposite worlds. It was green only because the stale empty cache forced the legacy path where the capability mocks were never consulted.buildInforeports API 24, so with the cache fixed the provider reads capabilities. Its mocks are now consistent with its name, in its own commit so the change is not buried in the fix.Full
sentry-android-coresuite: 1640 tests, 0 failures.apiDumpproduces no diff —io.sentry.android.core.internalis inapiValidation.ignoredPackages.📝 Checklist
sendDefaultPIIis enabled.