fix(NetworkChangeManager): classify transport from activeNetwork, not per-callback caps#903
Draft
torlando-agent[bot] wants to merge 2 commits into
Conversation
… per-callback caps `onCapabilitiesChanged` was calling `currentTransportOf(networkCapabilities)`, classifying off the per-callback caps. The `NetworkCallback` is registered against `NET_CAPABILITY_INTERNET`, which fires for every network matching the request — not just the system-default route. On a dual-radio device (Wi-Fi default + cellular kept up as backup) the cellular network's capability update was getting classified as `CELLULAR` and emitted as a transport change while Wi-Fi remained the active route. Switch to the existing `currentTransportOf(connectivityManager)` helper that resolves `connectivityManager.activeNetwork` first — same fix PR #896 applied to `InterfaceTransportObserver`. The activeNetwork read can race with callback delivery, but the OS snapshot is always self-consistent and the next callback re-classifies if the active route flipped between the two reads. Updates the existing capability-driven tests to stub `activeNetwork` + `getNetworkCapabilities(network)` and adds a dual-radio repro test that fails before the fix. Closes #898 Co-Authored-By: Claude claude-opus-4-7 <noreply@anthropic.com>
Contributor
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This PR's implementer pass inadvertently reverted the terminal-state matcher in `viewModel init triggers startServer ...` (commit d1ca233, PR #891 / issue #883). The restored strict `assertFalse(isServerRunning)` flaked on the very next CI run on Linux — exactly the behaviour PR #891 documented and fixed: `getLocalIpAddress()` returns a non-loopback IPv4 in the GH Actions Ubuntu image, the server actually binds, the assertion flips red. This change is also wholly out of scope for the issue #898 plan, which only touches `NetworkChangeManager`. Restore main's version verbatim; verified locally. Co-Authored-By: Claude claude-opus-4-7 <noreply@anthropic.com>
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.
Implements the approved plan for #898.
Summary
NetworkChangeManager.onCapabilitiesChangedatapp/src/main/java/network/columba/app/service/manager/NetworkChangeManager.kt:104callsemitTransportIfChanged(currentTransportOf(networkCapabilities)), classifying the transport from the per-callbackNetworkCapabilities. TheNetworkCallbackis registered againstNET_CAPABILITY_INTERNET, which fires for every network matching the request — not just the system-default route. On a dual-radio device (Wi-Fi default + cellular kept up as backup) the cellular network's capability update gets classified asCELLULARand emitted as a transport change while Wi-Fi remains the active route.Currently latent:
ServiceModule.createManagersis invoked fromReticulumService.onCreate(ReticulumService.kt:59) without anonTransportChangedconsumer, so the misclassified emission goes nowhere. Should be fixed before any service-process consumer relies on the callback.Changes
app/src/main/java/network/columba/app/service/manager/NetworkChangeManager.kt—onCapabilitiesChangednow classifies the transport via the existingcurrentTransportOf(connectivityManager)helper (readsconnectivityManager.activeNetworkthen its capabilities), mirroring the PR feat(InterfaceConfig): per-interface Wi-Fi/cellular network restriction #896 fix inInterfaceTransportObserver. The per-callbacknetwork/networkCapabilitiesparameters remain (still feed theLog.vline and are required by the framework signature). Comment block above the call was rewritten to describe the activeNetwork-classification rationale.app/src/test/java/network/columba/app/service/manager/NetworkChangeManagerTest.kt— capability-driven tests now stubconnectivityManager.activeNetworkandconnectivityManager.getNetworkCapabilities(network)before eachonCapabilitiesChanged(...)invocation. AddedonCapabilitiesChanged cellular backup on wifi default does not flip transportcovering the dual-radio repro.Test plan
./gradlew :app:testNoSentryDebugUnitTest --tests "network.columba.app.service.manager.NetworkChangeManagerTest"— 18/18 pass, including the new dual-radio repro../gradlew ktlintCheck detekt cpdCheck --continue— BUILD SUCCESSFUL on this branch's diff../gradlew :app:assembleNoSentryDebug— APK built clean for install verification.onTransportChanged. Behaviour change is purely classification source, observable only through the existing unit harness.Implementer notes
NetworkCallbackon its ownConnectivityThread, andactiveNetworkis the OS's atomic snapshot). If the active route flipped between callback firing and our read, we just see the newer value, which is also correct; the next callback re-classifies. Same race the PR feat(InterfaceConfig): per-interface Wi-Fi/cellular network restriction #896 InterfaceTransportObserver fix accepts.network: NetworkandnetworkCapabilities: NetworkCapabilitiesparameters ononCapabilitiesChangedstay — still feed theLog.vdebug line and are required by the framework signature. Production now ignoresnetworkCapabilitiesfor classification but the parameter is still semantically there.🤖 Generated with Claude Code