Skip to content

Fix race condition causing false ECONNREFUSED error - #77

Open
LandonMoran wants to merge 1 commit into
mainfrom
fix/issue-70-mdns-race-condition
Open

Fix race condition causing false ECONNREFUSED error#77
LandonMoran wants to merge 1 commit into
mainfrom
fix/issue-70-mdns-race-condition

Conversation

@LandonMoran

Copy link
Copy Markdown
Collaborator

Summary

Fixes a race condition in AdbDialogFragment where two concurrent paths can each launch a separate StarterActivity, producing a spurious ECONNREFUSED error dialog even though Shizuku successfully starts.

Closes #70.

Root Cause

AdbDialogFragment.onDialogShow() starts three things simultaneously:

  1. mDNS discovery (adbMdns.start()) — resolves _adb-tls-connect._tcp and calls startAndDismiss(discoveredPort) via LiveData observer
  2. tryTcpModePortFirst() — if TCP mode is on and port 5555 is live, calls startAndDismiss(5555)
  3. Neutral button — if the user taps the "5555" button, calls startAndDismiss(5555)

startAndDismiss() has no idempotency guard. If mDNS resolves a dynamic wireless debugging port (e.g. 37021) around the same time the user taps the neutral button (or tryTcpModePortFirst fires), two StarterActivity instances are launched with different ports:

  • The dynamic-port attempt succeeds → Shizuku starts
  • The 5555 attempt fails with ECONNREFUSED after exhausting 8 retries → shows error dialog

The user sees the error, swipes the app away, reopens it, and finds Shizuku running — exactly matching the issue report.

Fix

Add a one-shot guard in startAndDismiss():

private var startCommitted = false

private fun startAndDismiss(port: Int) {
    if (!isValidAdbPort(port)) return
    if (startCommitted) return

    startCommitted = true
    adbMdns.stop()
    // ... launch StarterActivity and dismiss
}

Port validation happens before the guard so an invalid port never locks out a valid second attempt. adbMdns.stop() is called immediately after committing to stop further mDNS callbacks. A plain Boolean is sufficient — all three call sites (button click, LiveData observer, withContext(Dispatchers.Main)) execute on the main thread.

adbMdns.stop() is already called in onDismiss(), and AdbMdns.stop() is idempotent (if (!running) return), so calling it earlier is safe.

Changes

  • AdbDialogFragment.kt — 5 lines added (1 field + 4 lines in startAndDismiss)

Testing

⚠️ Not compiled or device-tested — no Android SDK on the development machine. The fix is 5 additive lines with no control flow changes to existing code. The guard pattern is a standard one-shot boolean, and the idempotency of adbMdns.stop() was verified by reading the source.

What still needs testing:

  • Compilation with Android SDK
  • Verify that when mDNS resolves and the neutral button is tapped near-simultaneously, only one StarterActivity launches
  • Verify the error dialog no longer appears when the dynamic-port path succeeds

AdbDialogFragment.onDialogShow() launches mDNS discovery, a TCP port
check, and a LiveData observer that can all call startAndDismiss()
concurrently. When mDNS resolves a dynamic wireless debugging port
and the user taps the neutral button (or tryTcpModePortFirst fires)
around the same time, two StarterActivity instances launch with
different ports. One succeeds (dynamic port) while the other fails
with ECONNREFUSED (port 5555 not live), producing a spurious error
dialog even though Shizuku started successfully.

Fix: add a one-shot guard in startAndDismiss(). Validate the port
first, then check the guard, then set it and stop mDNS discovery
before launching StarterActivity. A plain Boolean is sufficient
since all call sites are on the main thread (button click, LiveData
observer, withContext(Dispatchers.Main)).

Closes #70
@github-actions github-actions Bot added the good-pr A solid PR: clear diff, solves the stated problem, follows the repo's conventions label Aug 8, 2026
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

🤖 AI Triage

Confirmed in manager/src/main/java/moe/shizuku/manager/home/AdbDialogFragment.kt: adding the startCommitted guard and calling adbMdns.stop() prevents concurrent start attempts from launching multiple StarterActivity instances.

Note: this is an automated AI bot (Gemini); its verdict isn't 100% accurate and can be wrong. The bot doesn't read or reply to further comments in this thread — if it got this wrong, ping a human maintainer.

@LandonMoran
LandonMoran requested a review from HmnDev-Tech August 8, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

good-pr A solid PR: clear diff, solves the stated problem, follows the repo's conventions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

​[Bug] False-positive ECONNREFUSED error when starting via Wireless ADB on port 5555 (Service actually starts)

1 participant