Android NSD - hangs / runs forever - device specific?
Asked Answered
T

0

20

I'm attempting to perform service discovery on my company's proprietary hardware device which hosts its own WiFi network and advertises a bonjour service over that network. When I'm connected to that WiFi network, I'm using NSD to discover that service and resolve it.

The code is pretty simple and generally works great and quickly. Except I'm having a fairly recurring issue (~5 out of 10 attempts) so far localized to my Samsung S8+. Cannot reproduce on a S6 or Pixel XL so far.

The issue is that DiscoveryListener never does anything beyond onDiscoveryStarted(), it just runs forever. If I kill the app and start over, sometimes it works sometimes it continues to hang.

It's like there's a blocked thread or something, but there is no useful info in the logs (that I can find) and nothing I have yet found to latch on to as something I can do to recover from this.

I have added a timeout that will stop the listener after 30 seconds, but generally when I retry after that it still doesn't work.

The service discovery process is wrapped in a LiveData, which starts it upon active.

    const val SERVICE_TYPE_FOO = "_foo._tcp."

    private val serviceDiscoveryListener = ServiceDiscoveryListener()

    override fun onActive() {
        super.onActive()
        stopRunnable = StopDiscoveryRunnable()
        nsdManager.discoverServices(
            SERVICE_TYPE_FOO,
            NsdManager.PROTOCOL_DNS_SD,
            serviceDiscoveryListener
        )
        handler.postDelayed(stopRunnable, SERVICE_DISCOVERY_TIMEOUT_MS)
    }

private lateinit var stopRunnable: Runnable

private inner class StopDiscoveryRunnable : Runnable {
    override fun run() {
        try {
            nsdManager.stopServiceDiscovery(serviceDiscoveryListener)
            Timber.w("service discovery timed out")
            postValue(ServiceDiscoveryState.Error)
        } catch (e: Throwable) {
            // no-op
        }
    }
}

The listeners are very simple...

private inner class ServiceDiscoveryListener : NsdManager.DiscoveryListener {
    override fun onServiceFound(serviceInfo: NsdServiceInfo?) {
        if (serviceInfo != null) {
            if (serviceInfo.serviceName.isNotEmpty() &&
                serviceInfo.serviceName.containsFoo()
            ) {
                nsdManager.resolveService(serviceInfo, ResolveListener())
                handler.removeCallbacks(stopRunnable)
            }
        }
    }

    override fun onStopDiscoveryFailed(serviceType: String?, errorCode: Int) {
        Timber.d("stop discovery failed")
        nsdManager.stopServiceDiscovery(this)
        postValue(ServiceDiscoveryState.Error)
    }

    override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) {
        Timber.d("start discovery failed")
        nsdManager.stopServiceDiscovery(this)
        postValue(ServiceDiscoveryState.Error)
    }

    override fun onDiscoveryStarted(serviceType: String?) {
        Timber.d("discovery started")
    }

    override fun onDiscoveryStopped(serviceType: String?) {
        Timber.d("discovery stopped")
    }

    override fun onServiceLost(serviceInfo: NsdServiceInfo?) {
        Timber.d("discovery service lost")
        postValue(ServiceDiscoveryState.Error)
    }
}

private inner class ResolveListener : NsdManager.ResolveListener {
    override fun onResolveFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
        Timber.d("resolve failed. errorCode = $errorCode")
        if (errorCode != NsdManager.FAILURE_ALREADY_ACTIVE) {
            Timber.d("not already active, so error")
            postValue(ServiceDiscoveryState.Error)
        } else {
            Timber.d("already active")
        }
    }

    override fun onServiceResolved(serviceInfo: NsdServiceInfo?) {
        if (serviceInfo != null) {
            Timber.d("resolved service: ${serviceInfo.serviceName} host: ${serviceInfo.host}:${serviceInfo.port} type: ${serviceInfo.serviceType}")
            postValue(ServiceDiscoveryState.FooService(serviceInfo))
        }
    }
}

I have not been able to find much of value in the logs, although I did see this one message when things were not working. Attempts to search for answers about this have not been fruitful.

04-26 13:50:14.541 953-1218/? W//system/bin/netd: dnssd_clientstub DNSServiceProcessResult called with invalid DNSServiceRef 0x72bb818000 FFFFFFFF DDDDDDDD

What other information can I provide?

I've reproduced the hang using Andriy Druk's bonjour Service Browser sample code on my S8+, which doesn't even use NSD... so it seems that perhaps the problem is in the core mDNS code and not specific to Google's NSD implementation?

Is there some system software or bloatware on the S8+ that may be interfering with my use of NSD or mDNS - thread blocking or something?

Titanesque answered 2/5, 2018 at 16:17 Comment(3)
I got my hands on another S8+, and it works great on that one. Difference? Main thing is that this second phone is running Android 7.1.1 while the original one is 8.0.0. Doesn't solve my problem, but another data point. Also tested on an S9 and it doesn't exhibit this issue so far, although the service discovery can be slow, much slower than on other devices.Titanesque
Issue has now been seen on the S9 as well :/Titanesque
Have similar issues on a Galaxy S7, running Android 8.0 with Samsung Experience 9.0. Running the exact same app on the same network on a Huawei P20 Lite I have no problems with service discovery.Wakefield

© 2022 - 2024 — McMap. All rights reserved.