Four related problems in how MainActivity owns the WebServer (MainActivity.java).
-
Dead non-null server after a double-start failure (lines 693-713): if binding 22222 fails and the port-0 fallback also fails, server is left referencing a constructed-but-never-started instance and is never reset to null. Every later onResume then logs "already running" and never retries; a GET_PORT reply sends getListeningPort() = -1, so the watch tries http://127.0.0.1:-1/. Fix: set server = null in the inner catch.
-
New activity instance races the old one for 22222 (lines 436-451,579-587; AndroidManifest, no launchMode): ACTION_SEND/ACTION_VIEW start a second MainActivity; Android resumes the new instance before stopping the old, so the new startWebServer finds 22222 still bound and silently falls back to a dynamic port — which the watch (hardcoded 22222 fallback) can't find. Fix: android:launchMode="singleTask" + onNewIntent, or move the server into a Service.
-
Sticky intent re-processed every resume aborts in-flight downloads (lines 469-485,606-634; mUris never cleared): processIntent(getIntent()) runs on every onResume, re-populates mUris, unconditionally server.stop()s (closing the watch's open connection), and deletes+recreates the temp dir. A permission dialog or focus change mid-download kills the transfer. Fix: process the intent once (setIntent(new Intent()) or a consumed flag) and only restart when the URI set changed.
-
Server dies on background (lines 579-587): onStop stops the server and clears the temp dir regardless of in-flight transfers, so backgrounding the app mid-download aborts it — directly counter to the app's purpose (slow BLE-proxied downloads the user waits on). Fix: host the WebServer in a foreground Service, or defer stop while connections are open.
Items 1,3,4 confidence HIGH; item 2 MEDIUM (lifecycle ordering certain, watch-side symptom depends on which listener is live).
Four related problems in how MainActivity owns the WebServer (MainActivity.java).
Dead non-null server after a double-start failure (lines 693-713): if binding 22222 fails and the port-0 fallback also fails,
serveris left referencing a constructed-but-never-started instance and is never reset to null. Every lateronResumethen logs "already running" and never retries; a GET_PORT reply sendsgetListeningPort()= -1, so the watch trieshttp://127.0.0.1:-1/. Fix: setserver = nullin the inner catch.New activity instance races the old one for 22222 (lines 436-451,579-587; AndroidManifest, no launchMode):
ACTION_SEND/ACTION_VIEWstart a second MainActivity; Android resumes the new instance before stopping the old, so the newstartWebServerfinds 22222 still bound and silently falls back to a dynamic port — which the watch (hardcoded 22222 fallback) can't find. Fix:android:launchMode="singleTask"+onNewIntent, or move the server into a Service.Sticky intent re-processed every resume aborts in-flight downloads (lines 469-485,606-634;
mUrisnever cleared):processIntent(getIntent())runs on everyonResume, re-populatesmUris, unconditionallyserver.stop()s (closing the watch's open connection), and deletes+recreates the temp dir. A permission dialog or focus change mid-download kills the transfer. Fix: process the intent once (setIntent(new Intent())or a consumed flag) and only restart when the URI set changed.Server dies on background (lines 579-587):
onStopstops the server and clears the temp dir regardless of in-flight transfers, so backgrounding the app mid-download aborts it — directly counter to the app's purpose (slow BLE-proxied downloads the user waits on). Fix: host the WebServer in a foreground Service, or defer stop while connections are open.Items 1,3,4 confidence HIGH; item 2 MEDIUM (lifecycle ordering certain, watch-side symptom depends on which listener is live).