fix(android): pass PROTOCOL_DNS_SD so mDNS registration works
Android 16 added NsdManager.checkProtocol(): registerService with protocol 0 threw IllegalArgumentException: Unsupported protocol — silently, since the old code swallowed the exception into a dead reflection fallback and start() overwrote the failure status with the Listening line. mDNS never actually advertised (the Phase 8 session streamed via --peer, masking it). Also advertise after the listening status so a registration failure stays visible, and log the exception and the registration success. Validated live on the Fairphone 6: the desktop --discover lists the phone and a full --send session decodes (in-band SPS 320x240 -> 2496x1040) and renders.
This commit is contained in:
@@ -304,6 +304,32 @@ Validation: `gradle :app:assembleDebug :app:testDebugUnitTest` green
|
||||
(25/25, full --rerun-tasks rebuild, only two pre-existing warnings);
|
||||
`meson test` 5/5 unchanged (no C++ touched).
|
||||
|
||||
### Discovery bug found and fixed on-device (2026-09-10, same day)
|
||||
|
||||
The user reported the desktop sender never discovered the phone.
|
||||
Root cause (found live on the Fairphone 6): `registerService(info, 0,
|
||||
listener)` — Android 16's `NsdManager.checkProtocol()` rejects protocol
|
||||
`0` with `IllegalArgumentException: Unsupported protocol`. The old code
|
||||
swallowed it into the dead reflection fallback, and `start()` posted the
|
||||
"Listening…" status AFTER advertiseNsd, overwriting the failure text — so
|
||||
mDNS never advertised and Phase 8's NSD validation was only ever "no
|
||||
crash" (8.5 streamed via --peer, masking it).
|
||||
|
||||
Fixes in `ReceiverPipeline.advertiseNsd()`/`start()`:
|
||||
- pass `NsdManager.PROTOCOL_DNS_SD`;
|
||||
- advertise AFTER the listening status so a failure stays visible;
|
||||
- `Log.e` the registration exception; `Log.i` on registered success.
|
||||
|
||||
On-device validation (adb, live): `mDNS registered:
|
||||
screencast._screencast._tcp` in logcat; `dumpsys servicediscovery` shows
|
||||
the active Advertiser (key diagnostic: `mClientRequests` empty == no
|
||||
request ever issued); desktop `avahi-browse` and `screencast --discover`
|
||||
list the phone at 192.168.178.29:5005; a 25s `--send --target monitor
|
||||
--peer 192.168.178.29:5005` session decoded (in-band SPS reconfigured
|
||||
320x240 → 2496x1040) and rendered (screencap mean brightness 0.51).
|
||||
Note: with both the Pi and the phone on the LAN, plain `--send` refuses
|
||||
(two receivers found) — target the phone with `--peer`.
|
||||
|
||||
Still open (accepted, needs a device or a new test dep):
|
||||
|
||||
- Untested on device: `setOutputSurface` mid-session (surface switch) and
|
||||
|
||||
@@ -110,11 +110,13 @@ class ReceiverPipeline(
|
||||
|
||||
running = true
|
||||
readerThread = Thread({ readLoop() }, "rtp-reader").also { it.start() }
|
||||
advertiseNsd(signalingPort)
|
||||
// Advertise AFTER the listening status: a registration failure must
|
||||
// not be overwritten by it (both post to the same overlay).
|
||||
onStatus(
|
||||
"Listening on $localIp (media :$udpPort, signaling :$signalingPort)\n" +
|
||||
"Waiting for a sender… (fall back to: screencast --send --peer $localIp:$signalingPort)",
|
||||
)
|
||||
advertiseNsd(signalingPort)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,6 +347,7 @@ class ReceiverPipeline(
|
||||
val listener = object : NsdManager.RegistrationListener {
|
||||
override fun onServiceRegistered(serviceInfo: NsdServiceInfo) {
|
||||
// Registered: the sender's mDNS browser should see it now.
|
||||
android.util.Log.i(TAG, "mDNS registered: ${serviceInfo.serviceName}.${SERVICE_TYPE}")
|
||||
}
|
||||
|
||||
override fun onServiceUnregistered(serviceInfo: NsdServiceInfo) = Unit
|
||||
@@ -356,9 +359,14 @@ class ReceiverPipeline(
|
||||
override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) = Unit
|
||||
}
|
||||
try {
|
||||
nsdManager.registerService(info, 0, listener)
|
||||
// PROTOCOL_DNS_SD is mandatory on API 36: NsdManager.checkProtocol()
|
||||
// rejects anything else (the historical 0 threw
|
||||
// "IllegalArgumentException: Unsupported protocol", which the
|
||||
// old code swallowed — mDNS never advertised on this phone).
|
||||
nsdManager.registerService(info, NsdManager.PROTOCOL_DNS_SD, listener)
|
||||
registrationListener = listener
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e(TAG, "mDNS registration failed", e)
|
||||
onStatus("mDNS unavailable (${e.message}) — reach this receiver with --peer $localIp:$port")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,13 @@ existing signaling + RTP protocol; no C++ changes.
|
||||
depacketizer — 25 tests green)
|
||||
- [x] 8.3 Signaling + NSD validated on device (offer → answer over the
|
||||
network; mDNS registration via the API-36 RegistrationListener)
|
||||
— **correction 2026-09-10**: registration had in fact never
|
||||
succeeded (registerService passed protocol `0`, which API 36
|
||||
rejects with "Unsupported protocol"; the swallowed failure was
|
||||
hidden behind the "Listening…" status). Fixed with
|
||||
`PROTOCOL_DNS_SD` + real on-device validation: desktop
|
||||
`--discover` lists the phone and a live `--send` session
|
||||
decodes and renders.
|
||||
- [x] 8.4 MediaCodec decode + Surface render validated on device (in-band
|
||||
SPS sizing, letterbox fit)
|
||||
- [x] 8.5 End-to-end on a Fairphone 6 (Android 16): streaming, letterboxed
|
||||
|
||||
@@ -240,6 +240,16 @@ Platform quirks found while validating (Android 16 / API 36):
|
||||
|
||||
- `android.permission.INTERNET` is required — NsdService rejects
|
||||
registration without it.
|
||||
- `NsdManager.registerService` on Android 16 validates the protocol
|
||||
argument (`NsdManager.checkProtocol`): the historical `0` throws
|
||||
`IllegalArgumentException: Unsupported protocol` — pass
|
||||
`NsdManager.PROTOCOL_DNS_SD`. This was silent for a long time: the app
|
||||
swallowed the exception and the failure status was overwritten by the
|
||||
"Listening…" line, so mDNS never advertised even though the UI looked
|
||||
fine (discovery only worked via `--peer`). Diagnosed via
|
||||
`adb shell dumpsys servicediscovery` (the client's `mClientRequests`
|
||||
stays empty when no request was ever issued) plus logging the
|
||||
exception.
|
||||
- `DatagramSocket.localPort` gives the bound port; `.port` is -1 for
|
||||
unconnected datagram sockets, and `.localAddress` is an `InetAddress`
|
||||
(Inet6Address on Android), not an `InetSocketAddress`.
|
||||
|
||||
Reference in New Issue
Block a user