NsdManager 发现不适用于 Android 9

NsdManager discovery does not work on Android 9

我已经尝试了很长时间,让 NsdManager 发现功能在 Android 9 上运行。它可以在之前的任何 Android 版本上运行,没有任何问题。

出于测试目的,我使用了这个简单的代码片段,清单中有 "INTERNET" 的权限。

var nsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager

nsdManager.discoverServices("_https._tcp", NsdManager.PROTOCOL_DNS_SD, object: NsdManager.DiscoveryListener {
    override fun onDiscoveryStarted(serviceType: String?) {
        println("DEBUG: onDiscoveryStarted $serviceType") 
    }
    override fun onDiscoveryStopped(serviceType: String?) {
        println("DEBUG: onDiscoveryStopped $serviceType") 
    }
    override fun onServiceFound(serviceInfo: NsdServiceInfo?) {
        println("DEBUG: onServiceFound $serviceInfo") 
    }
    override fun onServiceLost(serviceInfo: NsdServiceInfo?) {
        println("DEBUG: onServiceLost $serviceInfo") 
    }
    override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) {
        println("DEBUG: onStartDiscoveryFailed $serviceType $errorCode") 
    }
    override fun onStopDiscoveryFailed(serviceType: String?, errorCode: Int) {
        println("DEBUG: onStopDiscoveryFailed $serviceType $errorCode") 
    }
})

执行此代码时,我在日志中看不到任何调试或错误消息。

通过 Wireshark,我可以看到针对“_https._tcp.local”的广播 mDNS 标准查询以及来自所有预期设备的所有相应响应。 Android 似乎没有收到响应,因为函数 "onServiceFound" 从未被调用过。事实上,除了 "onDiscoveryStared" 之外,没有函数被调用过。

我在 Android 9 上没有看到任何关于为什么这不起作用的变化,我们很高兴提供任何帮助。也许我做错了什么或错过了当前 Android 版本中的任何权限更改。

更新:在当前 Android 9 beta 的 Samsung S9+ 上,服务发现工作没有任何问题。在另一侧的 Pixel 3XL 上它不起作用。

好的,我已经在旧的 Github issue. Apparently you need to have the wifi multicast permission and need to acquire a MulticastLock 中找到了这个问题的解决方案来接收多播消息。

我还没有找到关于此的任何文档,这似乎不是 Android 9 的特定问题,而是 Google 像素默认设置禁用所有监听多播包以保留电池。

对于遇到此问题并且不想深入研究冗长问题的任何人,我添加了以下代码:

AndroidManifest.xml:

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

浏览器:

WifiManager wifi = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wifi.createMulticastLock("multicastLock");
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
.
.
multicastLock.release(); // release after browsing