v0.6.0 — Rebuild Android NSD around explicit ownership and readable flow - #24
Merged
Merged
Conversation
This is not a normal “fix one bug” or “add one feature” update. For around ten days, Sync360’s nearby-network code has been rebuilt with a different goal: not to make the code look smaller, not to add an abstraction layer, and not to pretend that the old implementation had no useful defensive ideas. The goal was to make the Android NSD implementation understandable enough that it can genuinely be owned, maintained, questioned, and improved by a human who reads it later. The previous Android implementation was defensive and tried to cover many difficult situations. Some of that work was valuable. But it had become hard to follow as one story. It did not read like “start discovery, receive a device, resolve it if needed, publish it, remove it, and stop it.” It read more like a collection of mechanisms whose purpose had to be rediscovered before changing anything safely. This release replaces that relationship with a more direct one. The shared NetworkServices contract now says exactly what the app does: - start discovery - stop discovery - start advertising - stop advertising Android then owns the Android-specific details behind those four actions. On Tiramisu SDK extension 22 and newer, discovery uses DiscoveryRequest and one ServiceInfoCallback. That callback owns the modern discovery path: it receives device updates, device loss, and callback registration/unregistration outcomes. Below extension 22, discovery uses DiscoveryListener and the older one-shot resolveService API. Legacy resolution is deliberately serialized with a queue because only one legacy resolve is allowed to be active at a time. A resolver slot is released after success, failure, malformed data, a null result, self-device filtering, or a synchronous resolveService exception, so the next queued service can continue. Advertising is separate from discovery but shared across Android versions through one RegistrationListener. Discovery and advertising now expose their real platform status instead of hiding state behind a generic controller error message or a second “enabled” boolean that can disagree with Android. The shared controller was simplified heavily. It starts the HTTP and TCP listeners once, asks NetworkServices to start or stop nearby sharing, and exposes the platform status flows and nearby-device list. The Send UI now chooses Start, Stop, or Try again from the actual discovery status. It does not maintain a duplicate story about whether discovery should theoretically be enabled. Desktop and iOS implementations were also split into the same four explicit operations. This is not an attempt to make every platform implementation identical. It is an attempt to make the shared contract honest while allowing each platform to keep its own native behavior. This change may contain more code in some native places than a compact abstraction would. That is intentional. Explicit listener ownership, callbacks, queues, status changes, and cleanup are easier to reason about than clever code which is shorter but requires faith to modify. This is a developer-ownership release. The normal Android foreground path has been manually observed: nearby devices appear, update, and disappear. It is not a claim that every lifecycle, failure, multi-network, permission, or legacy NSD edge case is finished forever. Those are now visible, named problems in code that can be understood and improved deliberately instead of being hidden behind code we do not really own. Prepare Android, Desktop, and iOS package metadata as version 0.6.0. Android and iOS build numbers move to 10.
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.
Why this PR exists
This is a deliberately human-oriented refactor.
Sync360's nearby discovery code is important. It decides whether nearby devices appear, whether this device is visible to others, and whether the app can safely clean up and start again. It is also native Android code with asynchronous callbacks, legacy API paths, listener ownership, and lifecycle questions. That means it is exactly the kind of code that becomes risky when it works only because it was generated, copied, or made defensive in ways the current maintainer cannot comfortably explain.
The older Android NSD implementation was not treated as worthless. It was a useful reference and it contained defensive handling for difficult situations. But it was not the implementation I wanted to carry forward as the owner of this project. I wanted the code to read like the actual mental model of Sync360:
That simple story matters more here than reducing line count. Native networking code does not become easier merely because it is shorter. It becomes easier when each resource has an obvious owner, each callback has an obvious job, and future changes do not require trusting code that nobody fully understands.
What changed
The common
NetworkServicescontract is now split into four direct actions:startDiscovery()stopDiscovery()startAdvertising(httpServerPort, fileTransferPort)stopAdvertising()This removes the old combined start/stop operation and makes the contract reflect what the app actually owns: browsing nearby services and advertising this device are related, but they are still separate native operations with separate callbacks and failures.
Android now uses the rebuilt
AndroidNetworkServicesimplementation. Its intended discovery paths are explicit:DiscoveryRequestand oneServiceInfoCallbackfor modern combined discovery, resolution, updates, and loss.DiscoveryListenerplus the deprecated one-shotresolveService()API.RegistrationListener, shared by both discovery paths.Starting,FailedToStart,Running,Stopping, andCleanupFailed.The controller was simplified at the same time. It now starts the HTTP server and file-transfer receiver once, then directly asks the platform service to start or stop nearby sharing. It exposes the nearby-device list and the real platform discovery/registration statuses. The Send UI no longer keeps a separate
isDiscoveryEnabledboolean or a generic discovery-error string that can contradict Android's actual state.Instead, the UI has a direct relationship with discovery status:
Desktop and iOS were updated to implement the same explicit common contract. This is contract alignment, not a claim that every platform backend has received the same level of manual validation as Android.
Why this is intentionally not "just fewer lines"
The old implementation may have covered some edge cases more aggressively. This PR does not claim that every removed layer was bad or that the new implementation has magically solved every native networking problem.
The point is different: this code can now be read from top to bottom by the person responsible for it.
A future fix should begin with understandable questions:
That is a better foundation for reliability than an implementation which might be more defensive on paper but is too opaque to change with confidence.
Current confidence and honest limitations
Normal Android foreground discovery has been manually observed during this work: devices were added and removed in normal conditions.
This is still a preview release and not a claim of complete NSD maturity. Known follow-up areas include:
These are not hidden from this PR. They are simply not being solved by pretending this release is finished when its real achievement is clarity, ownership, and a much better base for deliberate future work.
Versioning