• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.net.cts
17 
18 import android.Manifest.permission.MANAGE_TEST_NETWORKS
19 import android.app.compat.CompatChanges
20 import android.net.ConnectivityManager
21 import android.net.ConnectivityManager.NetworkCallback
22 import android.net.InetAddresses.parseNumericAddress
23 import android.net.LinkAddress
24 import android.net.LinkProperties
25 import android.net.LocalSocket
26 import android.net.LocalSocketAddress
27 import android.net.Network
28 import android.net.NetworkAgentConfig
29 import android.net.NetworkCapabilities
30 import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
31 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
32 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
33 import android.net.NetworkCapabilities.TRANSPORT_TEST
34 import android.net.NetworkRequest
35 import android.net.TestNetworkInterface
36 import android.net.TestNetworkManager
37 import android.net.TestNetworkSpecifier
38 import android.net.connectivity.ConnectivityCompatChanges
39 import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted
40 import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped
41 import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.ServiceFound
42 import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.ServiceLost
43 import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.StartDiscoveryFailed
44 import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.StopDiscoveryFailed
45 import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.RegistrationFailed
46 import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.ServiceRegistered
47 import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.ServiceUnregistered
48 import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.UnregistrationFailed
49 import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ResolutionStopped
50 import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ResolveFailed
51 import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ServiceResolved
52 import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.StopResolutionFailed
53 import android.net.cts.NsdManagerTest.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.RegisterCallbackFailed
54 import android.net.cts.NsdManagerTest.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdated
55 import android.net.cts.NsdManagerTest.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdatedLost
56 import android.net.cts.NsdManagerTest.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.UnregisterCallbackSucceeded
57 import android.net.cts.util.CtsNetUtils
58 import android.net.nsd.NsdManager
59 import android.net.nsd.NsdManager.DiscoveryListener
60 import android.net.nsd.NsdManager.RegistrationListener
61 import android.net.nsd.NsdManager.ResolveListener
62 import android.net.nsd.NsdServiceInfo
63 import android.os.Build
64 import android.os.Handler
65 import android.os.HandlerThread
66 import android.os.Process.myTid
67 import android.platform.test.annotations.AppModeFull
68 import android.system.ErrnoException
69 import android.system.Os
70 import android.system.OsConstants.AF_INET6
71 import android.system.OsConstants.EADDRNOTAVAIL
72 import android.system.OsConstants.ENETUNREACH
73 import android.system.OsConstants.IPPROTO_UDP
74 import android.system.OsConstants.SOCK_DGRAM
75 import android.util.Log
76 import androidx.test.filters.SmallTest
77 import androidx.test.platform.app.InstrumentationRegistry
78 import com.android.compatibility.common.util.PollingCheck
79 import com.android.compatibility.common.util.PropertyUtil
80 import com.android.modules.utils.build.SdkLevel.isAtLeastU
81 import com.android.net.module.util.ArrayTrackRecord
82 import com.android.net.module.util.TrackRecord
83 import com.android.networkstack.apishim.NsdShimImpl
84 import com.android.networkstack.apishim.common.NsdShim
85 import com.android.testutils.ConnectivityModuleTest
86 import com.android.testutils.DevSdkIgnoreRule
87 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
88 import com.android.testutils.DevSdkIgnoreRunner
89 import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
90 import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
91 import com.android.testutils.TestableNetworkAgent
92 import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
93 import com.android.testutils.TestableNetworkCallback
94 import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk30
95 import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk33
96 import com.android.testutils.runAsShell
97 import com.android.testutils.tryTest
98 import com.android.testutils.waitForIdle
99 import java.io.File
100 import java.io.IOException
101 import java.net.Inet6Address
102 import java.net.InetAddress
103 import java.net.NetworkInterface
104 import java.net.ServerSocket
105 import java.nio.charset.StandardCharsets
106 import java.util.Random
107 import java.util.concurrent.Executor
108 import kotlin.math.min
109 import kotlin.test.assertEquals
110 import kotlin.test.assertFailsWith
111 import kotlin.test.assertNotNull
112 import kotlin.test.assertNull
113 import kotlin.test.assertTrue
114 import kotlin.test.fail
115 import org.junit.After
116 import org.junit.Assert.assertArrayEquals
117 import org.junit.Assert.assertFalse
118 import org.junit.Assert.assertTrue
119 import org.junit.Assume.assumeTrue
120 import org.junit.Before
121 import org.junit.Rule
122 import org.junit.Test
123 import org.junit.runner.RunWith
124 
125 private const val TAG = "NsdManagerTest"
126 private const val TIMEOUT_MS = 2000L
127 private const val NO_CALLBACK_TIMEOUT_MS = 200L
128 // Registration may take a long time if there are devices with the same hostname on the network,
129 // as the device needs to try another name and probe again. This is especially true since when using
130 // mdnsresponder the usual hostname is "Android", and on conflict "Android-2", "Android-3", ... are
131 // tried sequentially
132 private const val REGISTRATION_TIMEOUT_MS = 10_000L
133 private const val DBG = false
134 private const val TEST_PORT = 12345
135 
136 private val nsdShim = NsdShimImpl.newInstance()
137 
138 @AppModeFull(reason = "Socket cannot bind in instant app mode")
139 @RunWith(DevSdkIgnoreRunner::class)
140 @SmallTest
141 @ConnectivityModuleTest
142 @IgnoreUpTo(Build.VERSION_CODES.S_V2)
143 class NsdManagerTest {
144     // Rule used to filter CtsNetTestCasesMaxTargetSdkXX
145     @get:Rule
146     val ignoreRule = DevSdkIgnoreRule()
147 
148     private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
149     private val nsdManager by lazy { context.getSystemService(NsdManager::class.java) }
150 
151     private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
152     private val serviceName = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
153     private val serviceType = "_nmt%09d._tcp".format(Random().nextInt(1_000_000_000))
154     private val handlerThread = HandlerThread(NsdManagerTest::class.java.simpleName)
155     private val ctsNetUtils by lazy{ CtsNetUtils(context) }
156 
157     private lateinit var testNetwork1: TestTapNetwork
158     private lateinit var testNetwork2: TestTapNetwork
159 
160     private class TestTapNetwork(
161         val iface: TestNetworkInterface,
162         val requestCb: NetworkCallback,
163         val agent: TestableNetworkAgent,
164         val network: Network
165     ) {
166         fun close(cm: ConnectivityManager) {
167             cm.unregisterNetworkCallback(requestCb)
168             agent.unregister()
169             iface.fileDescriptor.close()
170             agent.waitForIdle(TIMEOUT_MS)
171         }
172     }
173 
174     private interface NsdEvent
175     private open class NsdRecord<T : NsdEvent> private constructor(
176         private val history: ArrayTrackRecord<T>,
177         private val expectedThreadId: Int? = null
178     ) : TrackRecord<T> by history {
179         constructor(expectedThreadId: Int? = null) : this(ArrayTrackRecord(), expectedThreadId)
180 
181         val nextEvents = history.newReadHead()
182 
183         override fun add(e: T): Boolean {
184             if (expectedThreadId != null) {
185                 assertEquals(expectedThreadId, myTid(), "Callback is running on the wrong thread")
186             }
187             return history.add(e)
188         }
189 
190         inline fun <reified V : NsdEvent> expectCallbackEventually(
191             timeoutMs: Long = TIMEOUT_MS,
192             crossinline predicate: (V) -> Boolean = { true }
193         ): V = nextEvents.poll(timeoutMs) { e -> e is V && predicate(e) } as V?
194                 ?: fail("Callback for ${V::class.java.simpleName} not seen after $timeoutMs ms")
195 
196         inline fun <reified V : NsdEvent> expectCallback(timeoutMs: Long = TIMEOUT_MS): V {
197             val nextEvent = nextEvents.poll(timeoutMs)
198             assertNotNull(nextEvent, "No callback received after $timeoutMs ms, expected " +
199                     "${V::class.java.simpleName}")
200             assertTrue(nextEvent is V, "Expected ${V::class.java.simpleName} but got " +
201                     nextEvent.javaClass.simpleName)
202             return nextEvent
203         }
204 
205         inline fun assertNoCallback(timeoutMs: Long = NO_CALLBACK_TIMEOUT_MS) {
206             val cb = nextEvents.poll(timeoutMs)
207             assertNull(cb, "Expected no callback but got $cb")
208         }
209     }
210 
211     private class NsdRegistrationRecord(expectedThreadId: Int? = null) : RegistrationListener,
212             NsdRecord<NsdRegistrationRecord.RegistrationEvent>(expectedThreadId) {
213         sealed class RegistrationEvent : NsdEvent {
214             abstract val serviceInfo: NsdServiceInfo
215 
216             data class RegistrationFailed(
217                 override val serviceInfo: NsdServiceInfo,
218                 val errorCode: Int
219             ) : RegistrationEvent()
220 
221             data class UnregistrationFailed(
222                 override val serviceInfo: NsdServiceInfo,
223                 val errorCode: Int
224             ) : RegistrationEvent()
225 
226             data class ServiceRegistered(override val serviceInfo: NsdServiceInfo) :
227                     RegistrationEvent()
228             data class ServiceUnregistered(override val serviceInfo: NsdServiceInfo) :
229                     RegistrationEvent()
230         }
231 
232         override fun onRegistrationFailed(si: NsdServiceInfo, err: Int) {
233             add(RegistrationFailed(si, err))
234         }
235 
236         override fun onUnregistrationFailed(si: NsdServiceInfo, err: Int) {
237             add(UnregistrationFailed(si, err))
238         }
239 
240         override fun onServiceRegistered(si: NsdServiceInfo) {
241             add(ServiceRegistered(si))
242         }
243 
244         override fun onServiceUnregistered(si: NsdServiceInfo) {
245             add(ServiceUnregistered(si))
246         }
247     }
248 
249     private class NsdDiscoveryRecord(expectedThreadId: Int? = null) :
250             DiscoveryListener, NsdRecord<NsdDiscoveryRecord.DiscoveryEvent>(expectedThreadId) {
251         sealed class DiscoveryEvent : NsdEvent {
252             data class StartDiscoveryFailed(val serviceType: String, val errorCode: Int) :
253                     DiscoveryEvent()
254 
255             data class StopDiscoveryFailed(val serviceType: String, val errorCode: Int) :
256                     DiscoveryEvent()
257 
258             data class DiscoveryStarted(val serviceType: String) : DiscoveryEvent()
259             data class DiscoveryStopped(val serviceType: String) : DiscoveryEvent()
260             data class ServiceFound(val serviceInfo: NsdServiceInfo) : DiscoveryEvent()
261             data class ServiceLost(val serviceInfo: NsdServiceInfo) : DiscoveryEvent()
262         }
263 
264         override fun onStartDiscoveryFailed(serviceType: String, err: Int) {
265             add(StartDiscoveryFailed(serviceType, err))
266         }
267 
268         override fun onStopDiscoveryFailed(serviceType: String, err: Int) {
269             add(StopDiscoveryFailed(serviceType, err))
270         }
271 
272         override fun onDiscoveryStarted(serviceType: String) {
273             add(DiscoveryStarted(serviceType))
274         }
275 
276         override fun onDiscoveryStopped(serviceType: String) {
277             add(DiscoveryStopped(serviceType))
278         }
279 
280         override fun onServiceFound(si: NsdServiceInfo) {
281             add(ServiceFound(si))
282         }
283 
284         override fun onServiceLost(si: NsdServiceInfo) {
285             add(ServiceLost(si))
286         }
287 
288         fun waitForServiceDiscovered(
289             serviceName: String,
290             serviceType: String,
291             expectedNetwork: Network? = null
292         ): NsdServiceInfo {
293             val serviceFound = expectCallbackEventually<ServiceFound> {
294                 it.serviceInfo.serviceName == serviceName &&
295                         (expectedNetwork == null ||
296                                 expectedNetwork == nsdShim.getNetwork(it.serviceInfo))
297             }.serviceInfo
298             // Discovered service types have a dot at the end
299             assertEquals("$serviceType.", serviceFound.serviceType)
300             return serviceFound
301         }
302     }
303 
304     private class NsdResolveRecord : ResolveListener,
305             NsdRecord<NsdResolveRecord.ResolveEvent>() {
306         sealed class ResolveEvent : NsdEvent {
307             data class ResolveFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int) :
308                     ResolveEvent()
309 
310             data class ServiceResolved(val serviceInfo: NsdServiceInfo) : ResolveEvent()
311             data class ResolutionStopped(val serviceInfo: NsdServiceInfo) : ResolveEvent()
312             data class StopResolutionFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int) :
313                     ResolveEvent()
314         }
315 
316         override fun onResolveFailed(si: NsdServiceInfo, err: Int) {
317             add(ResolveFailed(si, err))
318         }
319 
320         override fun onServiceResolved(si: NsdServiceInfo) {
321             add(ServiceResolved(si))
322         }
323 
324         override fun onResolutionStopped(si: NsdServiceInfo) {
325             add(ResolutionStopped(si))
326         }
327 
328         override fun onStopResolutionFailed(si: NsdServiceInfo, err: Int) {
329             super.onStopResolutionFailed(si, err)
330             add(StopResolutionFailed(si, err))
331         }
332     }
333 
334     private class NsdServiceInfoCallbackRecord : NsdShim.ServiceInfoCallbackShim,
335             NsdRecord<NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent>() {
336         sealed class ServiceInfoCallbackEvent : NsdEvent {
337             data class RegisterCallbackFailed(val errorCode: Int) : ServiceInfoCallbackEvent()
338             data class ServiceUpdated(val serviceInfo: NsdServiceInfo) : ServiceInfoCallbackEvent()
339             object ServiceUpdatedLost : ServiceInfoCallbackEvent()
340             object UnregisterCallbackSucceeded : ServiceInfoCallbackEvent()
341         }
342 
343         override fun onServiceInfoCallbackRegistrationFailed(err: Int) {
344             add(RegisterCallbackFailed(err))
345         }
346 
347         override fun onServiceUpdated(si: NsdServiceInfo) {
348             add(ServiceUpdated(si))
349         }
350 
351         override fun onServiceLost() {
352             add(ServiceUpdatedLost)
353         }
354 
355         override fun onServiceInfoCallbackUnregistered() {
356             add(UnregisterCallbackSucceeded)
357         }
358     }
359 
360     @Before
361     fun setUp() {
362         handlerThread.start()
363 
364         if (TestUtils.shouldTestTApis()) {
365             runAsShell(MANAGE_TEST_NETWORKS) {
366                 testNetwork1 = createTestNetwork()
367                 testNetwork2 = createTestNetwork()
368             }
369         }
370     }
371 
372     private fun createTestNetwork(): TestTapNetwork {
373         val tnm = context.getSystemService(TestNetworkManager::class.java)
374         val iface = tnm.createTapInterface()
375         val cb = TestableNetworkCallback()
376         val testNetworkSpecifier = TestNetworkSpecifier(iface.interfaceName)
377         cm.requestNetwork(NetworkRequest.Builder()
378                 .removeCapability(NET_CAPABILITY_TRUSTED)
379                 .addTransportType(TRANSPORT_TEST)
380                 .setNetworkSpecifier(testNetworkSpecifier)
381                 .build(), cb)
382         val agent = registerTestNetworkAgent(iface.interfaceName)
383         val network = agent.network ?: fail("Registered agent should have a network")
384 
385         cb.eventuallyExpect<LinkPropertiesChanged>(TIMEOUT_MS) {
386             it.lp.linkAddresses.isNotEmpty()
387         }
388 
389         // The network has no INTERNET capability, so will be marked validated immediately
390         // It does not matter if validated capabilities come before/after the link addresses change
391         cb.eventuallyExpect<CapabilitiesChanged>(TIMEOUT_MS, from = 0) {
392             it.caps.hasCapability(NET_CAPABILITY_VALIDATED)
393         }
394         return TestTapNetwork(iface, cb, agent, network)
395     }
396 
397     private fun registerTestNetworkAgent(ifaceName: String): TestableNetworkAgent {
398         val lp = LinkProperties().apply {
399             interfaceName = ifaceName
400         }
401 
402         val agent = TestableNetworkAgent(context, handlerThread.looper,
403                 NetworkCapabilities().apply {
404                     removeCapability(NET_CAPABILITY_TRUSTED)
405                     addTransportType(TRANSPORT_TEST)
406                     setNetworkSpecifier(TestNetworkSpecifier(ifaceName))
407                 }, lp, NetworkAgentConfig.Builder().build())
408         val network = agent.register()
409         agent.markConnected()
410         agent.expectCallback<OnNetworkCreated>()
411 
412         // Wait until the link-local address can be used. Address flags are not available without
413         // elevated permissions, so check that bindSocket works.
414         PollingCheck.check("No usable v6 address on interface after $TIMEOUT_MS ms", TIMEOUT_MS) {
415             // To avoid race condition between socket connection succeeding and interface returning
416             // a non-empty address list. Verify that interface returns a non-empty list, before
417             // trying the socket connection.
418             if (NetworkInterface.getByName(ifaceName).interfaceAddresses.isEmpty()) {
419                 return@check false
420             }
421 
422             val sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
423             tryTest {
424                 network.bindSocket(sock)
425                 Os.connect(sock, parseNumericAddress("ff02::fb%$ifaceName"), 12345)
426                 true
427             }.catch<ErrnoException> {
428                 if (it.errno != ENETUNREACH && it.errno != EADDRNOTAVAIL) {
429                     throw it
430                 }
431                 false
432             } cleanup {
433                 Os.close(sock)
434             }
435         }
436 
437         lp.setLinkAddresses(NetworkInterface.getByName(ifaceName).interfaceAddresses.map {
438             LinkAddress(it.address, it.networkPrefixLength.toInt())
439         })
440         agent.sendLinkProperties(lp)
441         return agent
442     }
443 
444     private fun makeTestServiceInfo(network: Network? = null) = NsdServiceInfo().also {
445         it.serviceType = serviceType
446         it.serviceName = serviceName
447         it.network = network
448         it.port = TEST_PORT
449     }
450 
451     @After
452     fun tearDown() {
453         if (TestUtils.shouldTestTApis()) {
454             runAsShell(MANAGE_TEST_NETWORKS) {
455                 // Avoid throwing here if initializing failed in setUp
456                 if (this::testNetwork1.isInitialized) testNetwork1.close(cm)
457                 if (this::testNetwork2.isInitialized) testNetwork2.close(cm)
458             }
459         }
460         handlerThread.waitForIdle(TIMEOUT_MS)
461         handlerThread.quitSafely()
462         handlerThread.join()
463     }
464 
465     @Test
466     fun testNsdManager() {
467         val si = NsdServiceInfo()
468         si.serviceType = serviceType
469         si.serviceName = serviceName
470         // Test binary data with various bytes
471         val testByteArray = byteArrayOf(-128, 127, 2, 1, 0, 1, 2)
472         // Test string data with 256 characters (25 blocks of 10 characters + 6)
473         val string256 = "1_________2_________3_________4_________5_________6_________" +
474                 "7_________8_________9_________10________11________12________13________" +
475                 "14________15________16________17________18________19________20________" +
476                 "21________22________23________24________25________123456"
477 
478         // Illegal attributes
479         listOf(
480                 Triple(null, null, "null key"),
481                 Triple("", null, "empty key"),
482                 Triple(string256, null, "key with 256 characters"),
483                 Triple("key", string256.substring(3),
484                         "key+value combination with more than 255 characters"),
485                 Triple("key", string256.substring(4), "key+value combination with 255 characters"),
486                 Triple("\u0019", null, "key with invalid character"),
487                 Triple("=", null, "key with invalid character"),
488                 Triple("\u007f", null, "key with invalid character")
489         ).forEach {
490             assertFailsWith<IllegalArgumentException>(
491                     "Setting invalid ${it.third} unexpectedly succeeded") {
492                 si.setAttribute(it.first, it.second)
493             }
494         }
495 
496         // Allowed attributes
497         si.setAttribute("booleanAttr", null as String?)
498         si.setAttribute("keyValueAttr", "value")
499         si.setAttribute("keyEqualsAttr", "=")
500         si.setAttribute(" whiteSpaceKeyValueAttr ", " value ")
501         si.setAttribute("binaryDataAttr", testByteArray)
502         si.setAttribute("nullBinaryDataAttr", null as ByteArray?)
503         si.setAttribute("emptyBinaryDataAttr", byteArrayOf())
504         si.setAttribute("longkey", string256.substring(9))
505         val socket = ServerSocket(0)
506         val localPort = socket.localPort
507         si.port = localPort
508         if (DBG) Log.d(TAG, "Port = $localPort")
509 
510         val registrationRecord = NsdRegistrationRecord()
511         // Test registering without an Executor
512         nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, registrationRecord)
513         val registeredInfo = registrationRecord.expectCallback<ServiceRegistered>(
514                 REGISTRATION_TIMEOUT_MS).serviceInfo
515 
516         // Only service name is included in ServiceRegistered callbacks
517         assertNull(registeredInfo.serviceType)
518         assertEquals(si.serviceName, registeredInfo.serviceName)
519 
520         val discoveryRecord = NsdDiscoveryRecord()
521         // Test discovering without an Executor
522         nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
523 
524         // Expect discovery started
525         discoveryRecord.expectCallback<DiscoveryStarted>()
526 
527         // Expect a service record to be discovered
528         val foundInfo = discoveryRecord.waitForServiceDiscovered(
529                 registeredInfo.serviceName, serviceType)
530 
531         // Test resolving without an Executor
532         val resolveRecord = NsdResolveRecord()
533         nsdManager.resolveService(foundInfo, resolveRecord)
534         val resolvedService = resolveRecord.expectCallback<ServiceResolved>().serviceInfo
535         assertEquals(".$serviceType", resolvedService.serviceType)
536         assertEquals(registeredInfo.serviceName, resolvedService.serviceName)
537 
538         // Check Txt attributes
539         assertEquals(8, resolvedService.attributes.size)
540         assertTrue(resolvedService.attributes.containsKey("booleanAttr"))
541         assertNull(resolvedService.attributes["booleanAttr"])
542         assertEquals("value", resolvedService.attributes["keyValueAttr"].utf8ToString())
543         assertEquals("=", resolvedService.attributes["keyEqualsAttr"].utf8ToString())
544         assertEquals(" value ",
545                 resolvedService.attributes[" whiteSpaceKeyValueAttr "].utf8ToString())
546         assertEquals(string256.substring(9), resolvedService.attributes["longkey"].utf8ToString())
547         assertArrayEquals(testByteArray, resolvedService.attributes["binaryDataAttr"])
548         assertTrue(resolvedService.attributes.containsKey("nullBinaryDataAttr"))
549         assertNull(resolvedService.attributes["nullBinaryDataAttr"])
550         assertTrue(resolvedService.attributes.containsKey("emptyBinaryDataAttr"))
551         // TODO: change the check to target SDK U when this is what the code implements
552         if (isAtLeastU()) {
553             assertArrayEquals(byteArrayOf(), resolvedService.attributes["emptyBinaryDataAttr"])
554         } else {
555             assertNull(resolvedService.attributes["emptyBinaryDataAttr"])
556         }
557         assertEquals(localPort, resolvedService.port)
558 
559         // Unregister the service
560         nsdManager.unregisterService(registrationRecord)
561         registrationRecord.expectCallback<ServiceUnregistered>()
562 
563         // Expect a callback for service lost
564         val lostCb = discoveryRecord.expectCallbackEventually<ServiceLost> {
565             it.serviceInfo.serviceName == serviceName
566         }
567         // Lost service types have a dot at the end
568         assertEquals("$serviceType.", lostCb.serviceInfo.serviceType)
569 
570         // Register service again to see if NsdManager can discover it
571         val si2 = NsdServiceInfo()
572         si2.serviceType = serviceType
573         si2.serviceName = serviceName
574         si2.port = localPort
575         val registrationRecord2 = NsdRegistrationRecord()
576         nsdManager.registerService(si2, NsdManager.PROTOCOL_DNS_SD, registrationRecord2)
577         val registeredInfo2 = registrationRecord2.expectCallback<ServiceRegistered>(
578                 REGISTRATION_TIMEOUT_MS).serviceInfo
579 
580         // Expect a service record to be discovered (and filter the ones
581         // that are unrelated to this test)
582         val foundInfo2 = discoveryRecord.waitForServiceDiscovered(
583                 registeredInfo2.serviceName, serviceType)
584 
585         // Resolve the service
586         val resolveRecord2 = NsdResolveRecord()
587         nsdManager.resolveService(foundInfo2, resolveRecord2)
588         val resolvedService2 = resolveRecord2.expectCallback<ServiceResolved>().serviceInfo
589 
590         // Check that the resolved service doesn't have any TXT records
591         assertEquals(0, resolvedService2.attributes.size)
592 
593         nsdManager.stopServiceDiscovery(discoveryRecord)
594 
595         discoveryRecord.expectCallbackEventually<DiscoveryStopped>()
596 
597         nsdManager.unregisterService(registrationRecord2)
598         registrationRecord2.expectCallback<ServiceUnregistered>()
599     }
600 
601     @Test
602     fun testNsdManager_DiscoverOnNetwork() {
603         // This test requires shims supporting T+ APIs (discovering on specific network)
604         assumeTrue(TestUtils.shouldTestTApis())
605 
606         val si = NsdServiceInfo()
607         si.serviceType = serviceType
608         si.serviceName = this.serviceName
609         si.port = 12345 // Test won't try to connect so port does not matter
610 
611         val registrationRecord = NsdRegistrationRecord()
612         val registeredInfo = registerService(registrationRecord, si)
613 
614         tryTest {
615             val discoveryRecord = NsdDiscoveryRecord()
616             nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
617                     testNetwork1.network, Executor { it.run() }, discoveryRecord)
618 
619             val foundInfo = discoveryRecord.waitForServiceDiscovered(
620                     serviceName, serviceType, testNetwork1.network)
621             assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo))
622 
623             // Rewind to ensure the service is not found on the other interface
624             discoveryRecord.nextEvents.rewind(0)
625             assertNull(discoveryRecord.nextEvents.poll(timeoutMs = 100L) {
626                 it is ServiceFound &&
627                         it.serviceInfo.serviceName == registeredInfo.serviceName &&
628                         nsdShim.getNetwork(it.serviceInfo) != testNetwork1.network
629             }, "The service should not be found on this network")
630         } cleanup {
631             nsdManager.unregisterService(registrationRecord)
632         }
633     }
634 
635     @Test
636     fun testNsdManager_DiscoverWithNetworkRequest() {
637         // This test requires shims supporting T+ APIs (discovering on network request)
638         assumeTrue(TestUtils.shouldTestTApis())
639 
640         val si = NsdServiceInfo()
641         si.serviceType = serviceType
642         si.serviceName = this.serviceName
643         si.port = 12345 // Test won't try to connect so port does not matter
644 
645         val handler = Handler(handlerThread.looper)
646         val executor = Executor { handler.post(it) }
647 
648         val registrationRecord = NsdRegistrationRecord(expectedThreadId = handlerThread.threadId)
649         val registeredInfo1 = registerService(registrationRecord, si, executor)
650         val discoveryRecord = NsdDiscoveryRecord(expectedThreadId = handlerThread.threadId)
651 
652         tryTest {
653             val specifier = TestNetworkSpecifier(testNetwork1.iface.interfaceName)
654             nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
655                     NetworkRequest.Builder()
656                             .removeCapability(NET_CAPABILITY_TRUSTED)
657                             .addTransportType(TRANSPORT_TEST)
658                             .setNetworkSpecifier(specifier)
659                             .build(),
660                     executor, discoveryRecord)
661 
662             val discoveryStarted = discoveryRecord.expectCallback<DiscoveryStarted>()
663             assertEquals(serviceType, discoveryStarted.serviceType)
664 
665             val serviceDiscovered = discoveryRecord.expectCallback<ServiceFound>()
666             assertEquals(registeredInfo1.serviceName, serviceDiscovered.serviceInfo.serviceName)
667             // Discovered service types have a dot at the end
668             assertEquals("$serviceType.", serviceDiscovered.serviceInfo.serviceType)
669             assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceDiscovered.serviceInfo))
670 
671             // Unregister, then register the service back: it should be lost and found again
672             nsdManager.unregisterService(registrationRecord)
673             val serviceLost1 = discoveryRecord.expectCallback<ServiceLost>()
674             assertEquals(registeredInfo1.serviceName, serviceLost1.serviceInfo.serviceName)
675             assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceLost1.serviceInfo))
676 
677             registrationRecord.expectCallback<ServiceUnregistered>()
678             val registeredInfo2 = registerService(registrationRecord, si, executor)
679             val serviceDiscovered2 = discoveryRecord.expectCallback<ServiceFound>()
680             assertEquals(registeredInfo2.serviceName, serviceDiscovered2.serviceInfo.serviceName)
681             assertEquals("$serviceType.", serviceDiscovered2.serviceInfo.serviceType)
682             assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceDiscovered2.serviceInfo))
683 
684             // Teardown, then bring back up a network on the test interface: the service should
685             // go away, then come back
686             testNetwork1.agent.unregister()
687             val serviceLost = discoveryRecord.expectCallback<ServiceLost>()
688             assertEquals(registeredInfo2.serviceName, serviceLost.serviceInfo.serviceName)
689             assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceLost.serviceInfo))
690 
691             val newAgent = runAsShell(MANAGE_TEST_NETWORKS) {
692                 registerTestNetworkAgent(testNetwork1.iface.interfaceName)
693             }
694             val newNetwork = newAgent.network ?: fail("Registered agent should have a network")
695             val serviceDiscovered3 = discoveryRecord.expectCallback<ServiceFound>()
696             assertEquals(registeredInfo2.serviceName, serviceDiscovered3.serviceInfo.serviceName)
697             assertEquals("$serviceType.", serviceDiscovered3.serviceInfo.serviceType)
698             assertEquals(newNetwork, nsdShim.getNetwork(serviceDiscovered3.serviceInfo))
699         } cleanupStep {
700             nsdManager.stopServiceDiscovery(discoveryRecord)
701             discoveryRecord.expectCallback<DiscoveryStopped>()
702         } cleanup {
703             nsdManager.unregisterService(registrationRecord)
704         }
705     }
706 
707     @Test
708     fun testNsdManager_DiscoverWithNetworkRequest_NoMatchingNetwork() {
709         // This test requires shims supporting T+ APIs (discovering on network request)
710         assumeTrue(TestUtils.shouldTestTApis())
711 
712         val si = NsdServiceInfo()
713         si.serviceType = serviceType
714         si.serviceName = this.serviceName
715         si.port = 12345 // Test won't try to connect so port does not matter
716 
717         val handler = Handler(handlerThread.looper)
718         val executor = Executor { handler.post(it) }
719 
720         val discoveryRecord = NsdDiscoveryRecord(expectedThreadId = handlerThread.threadId)
721         val specifier = TestNetworkSpecifier(testNetwork1.iface.interfaceName)
722 
723         tryTest {
724             nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
725                     NetworkRequest.Builder()
726                             .removeCapability(NET_CAPABILITY_TRUSTED)
727                             .addTransportType(TRANSPORT_TEST)
728                             // Specified network does not have this capability
729                             .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)
730                             .setNetworkSpecifier(specifier)
731                             .build(),
732                     executor, discoveryRecord)
733             discoveryRecord.expectCallback<DiscoveryStarted>()
734         } cleanup {
735             nsdManager.stopServiceDiscovery(discoveryRecord)
736             discoveryRecord.expectCallback<DiscoveryStopped>()
737         }
738     }
739 
740     private fun checkAddressScopeId(iface: TestNetworkInterface, address: List<InetAddress>) {
741         val targetSdkVersion = context.packageManager
742             .getTargetSdkVersion(context.applicationInfo.packageName)
743         if (targetSdkVersion <= Build.VERSION_CODES.TIRAMISU) {
744             return
745         }
746         val ifaceIdx = NetworkInterface.getByName(iface.interfaceName).index
747         address.forEach {
748             if (it is Inet6Address && it.isLinkLocalAddress) {
749                 assertEquals(ifaceIdx, it.scopeId)
750             }
751         }
752     }
753 
754     @Test
755     fun testNsdManager_ResolveOnNetwork() {
756         // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
757         assumeTrue(TestUtils.shouldTestTApis())
758 
759         val si = NsdServiceInfo()
760         si.serviceType = serviceType
761         si.serviceName = this.serviceName
762         si.port = 12345 // Test won't try to connect so port does not matter
763 
764         val registrationRecord = NsdRegistrationRecord()
765         val registeredInfo = registerService(registrationRecord, si)
766         tryTest {
767             val resolveRecord = NsdResolveRecord()
768 
769             val discoveryRecord = NsdDiscoveryRecord()
770             nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
771 
772             val foundInfo1 = discoveryRecord.waitForServiceDiscovered(
773                     serviceName, serviceType, testNetwork1.network)
774             assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo1))
775             // Rewind as the service could be found on each interface in any order
776             discoveryRecord.nextEvents.rewind(0)
777             val foundInfo2 = discoveryRecord.waitForServiceDiscovered(
778                     serviceName, serviceType, testNetwork2.network)
779             assertEquals(testNetwork2.network, nsdShim.getNetwork(foundInfo2))
780 
781             nsdShim.resolveService(nsdManager, foundInfo1, Executor { it.run() }, resolveRecord)
782             val cb = resolveRecord.expectCallback<ServiceResolved>()
783             cb.serviceInfo.let {
784                 // Resolved service type has leading dot
785                 assertEquals(".$serviceType", it.serviceType)
786                 assertEquals(registeredInfo.serviceName, it.serviceName)
787                 assertEquals(si.port, it.port)
788                 assertEquals(testNetwork1.network, nsdShim.getNetwork(it))
789                 checkAddressScopeId(testNetwork1.iface, it.hostAddresses)
790             }
791             // TODO: check that MDNS packets are sent only on testNetwork1.
792         } cleanupStep {
793             nsdManager.unregisterService(registrationRecord)
794         } cleanup {
795             registrationRecord.expectCallback<ServiceUnregistered>()
796         }
797     }
798 
799     @Test
800     fun testNsdManager_RegisterOnNetwork() {
801         // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
802         assumeTrue(TestUtils.shouldTestTApis())
803 
804         val si = NsdServiceInfo()
805         si.serviceType = serviceType
806         si.serviceName = this.serviceName
807         si.network = testNetwork1.network
808         si.port = 12345 // Test won't try to connect so port does not matter
809 
810         // Register service on testNetwork1
811         val registrationRecord = NsdRegistrationRecord()
812         registerService(registrationRecord, si)
813         val discoveryRecord = NsdDiscoveryRecord()
814         val discoveryRecord2 = NsdDiscoveryRecord()
815         val discoveryRecord3 = NsdDiscoveryRecord()
816 
817         tryTest {
818             // Discover service on testNetwork1.
819             nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
820                 testNetwork1.network, Executor { it.run() }, discoveryRecord)
821             // Expect that service is found on testNetwork1
822             val foundInfo = discoveryRecord.waitForServiceDiscovered(
823                 serviceName, serviceType, testNetwork1.network)
824             assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo))
825 
826             // Discover service on testNetwork2.
827             nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
828                 testNetwork2.network, Executor { it.run() }, discoveryRecord2)
829             // Expect that discovery is started then no other callbacks.
830             discoveryRecord2.expectCallback<DiscoveryStarted>()
831             discoveryRecord2.assertNoCallback()
832 
833             // Discover service on all networks (not specify any network).
834             nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
835                 null as Network? /* network */, Executor { it.run() }, discoveryRecord3)
836             // Expect that service is found on testNetwork1
837             val foundInfo3 = discoveryRecord3.waitForServiceDiscovered(
838                     serviceName, serviceType, testNetwork1.network)
839             assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo3))
840         } cleanupStep {
841             nsdManager.stopServiceDiscovery(discoveryRecord2)
842             discoveryRecord2.expectCallback<DiscoveryStopped>()
843         } cleanup {
844             nsdManager.unregisterService(registrationRecord)
845         }
846     }
847 
848     @Test
849     fun testNsdManager_RegisterServiceNameWithNonStandardCharacters() {
850         val serviceNames = "^Nsd.Test|Non-#AsCiI\\Characters&\\ufffe テスト 測試"
851         val si = NsdServiceInfo().apply {
852             serviceType = this@NsdManagerTest.serviceType
853             serviceName = serviceNames
854             port = 12345 // Test won't try to connect so port does not matter
855         }
856 
857         // Register the service name which contains non-standard characters.
858         val registrationRecord = NsdRegistrationRecord()
859         nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, registrationRecord)
860         registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
861 
862         tryTest {
863             // Discover that service name.
864             val discoveryRecord = NsdDiscoveryRecord()
865             nsdManager.discoverServices(
866                 serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord
867             )
868             val foundInfo = discoveryRecord.waitForServiceDiscovered(serviceNames, serviceType)
869 
870             // Expect that resolving the service name works properly even service name contains
871             // non-standard characters.
872             val resolveRecord = NsdResolveRecord()
873             nsdManager.resolveService(foundInfo, resolveRecord)
874             val resolvedCb = resolveRecord.expectCallback<ServiceResolved>()
875             assertEquals(foundInfo.serviceName, resolvedCb.serviceInfo.serviceName)
876         } cleanupStep {
877             nsdManager.unregisterService(registrationRecord)
878         } cleanup {
879             registrationRecord.expectCallback<ServiceUnregistered>()
880         }
881     }
882 
883     private fun checkConnectSocketToMdnsd(shouldFail: Boolean) {
884         val discoveryRecord = NsdDiscoveryRecord()
885         val localSocket = LocalSocket()
886         tryTest {
887             // Discover any service from NsdManager to enforce NsdService to start the mdnsd.
888             nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
889             discoveryRecord.expectCallback<DiscoveryStarted>()
890 
891             // Checks the /dev/socket/mdnsd is created.
892             val socket = File("/dev/socket/mdnsd")
893             val doesSocketExist = PollingCheck.waitFor(
894                 TIMEOUT_MS,
895                 {
896                     socket.exists()
897                 },
898                 { doesSocketExist ->
899                     doesSocketExist
900                 },
901             )
902 
903             // If the socket is not created, then no need to check the access.
904             if (doesSocketExist) {
905                 // Create a LocalSocket and try to connect to mdnsd.
906                 assertFalse("LocalSocket is connected.", localSocket.isConnected)
907                 val address = LocalSocketAddress("mdnsd", LocalSocketAddress.Namespace.RESERVED)
908                 if (shouldFail) {
909                     assertFailsWith<IOException>("Expect fail but socket connected") {
910                         localSocket.connect(address)
911                     }
912                 } else {
913                     localSocket.connect(address)
914                     assertTrue("LocalSocket is not connected.", localSocket.isConnected)
915                 }
916             }
917         } cleanup {
918             localSocket.close()
919             nsdManager.stopServiceDiscovery(discoveryRecord)
920             discoveryRecord.expectCallback<DiscoveryStopped>()
921         }
922     }
923 
924     /**
925      * Starting from Android U, the access to the /dev/socket/mdnsd is blocked by the
926      * sepolicy(b/265364111).
927      */
928     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
929     @Test
930     fun testCannotConnectSocketToMdnsd() {
931         val targetSdkVersion = context.packageManager
932                 .getTargetSdkVersion(context.applicationInfo.packageName)
933         assumeTrue(targetSdkVersion > Build.VERSION_CODES.TIRAMISU)
934         val firstApiLevel = min(PropertyUtil.getFirstApiLevel(), PropertyUtil.getVendorApiLevel())
935         // The sepolicy is implemented in the vendor image, so the access may not be blocked if
936         // the vendor image is not update to date.
937         assumeTrue(firstApiLevel > Build.VERSION_CODES.TIRAMISU)
938         checkConnectSocketToMdnsd(shouldFail = true)
939     }
940 
941     @Test @CtsNetTestCasesMaxTargetSdk33("mdnsd socket is accessible up to target SDK 33")
942     fun testCanConnectSocketToMdnsd() {
943         checkConnectSocketToMdnsd(shouldFail = false)
944     }
945 
946     @Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
947     fun testManagerCreatesLegacySocket() {
948         nsdManager // Ensure the lazy-init member is initialized, so NsdManager is created
949         val socket = File("/dev/socket/mdnsd")
950         val timeout = System.currentTimeMillis() + TIMEOUT_MS
951         while (!socket.exists() && System.currentTimeMillis() < timeout) {
952             Thread.sleep(10)
953         }
954         assertTrue("$socket was not found after $TIMEOUT_MS ms", socket.exists())
955     }
956 
957     // The compat change is part of a connectivity module update that applies to T+
958     @ConnectivityModuleTest @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
959     @Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
960     fun testManagerCreatesLegacySocket_CompatChange() {
961         // The socket may have been already created by some other app, or some other test, in which
962         // case this test cannot verify creation. At least verify that the compat change is
963         // disabled in a process with max SDK 30; unit tests already verify that start is requested
964         // when the compat change is disabled.
965         // Note that before T the compat constant had a different int value.
966         assertFalse(CompatChanges.isChangeEnabled(
967                 ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER))
968     }
969 
970     @Test
971     fun testStopServiceResolution() {
972         // This test requires shims supporting U+ APIs (NsdManager.stopServiceResolution)
973         assumeTrue(TestUtils.shouldTestUApis())
974 
975         val si = NsdServiceInfo()
976         si.serviceType = this@NsdManagerTest.serviceType
977         si.serviceName = this@NsdManagerTest.serviceName
978         si.port = 12345 // Test won't try to connect so port does not matter
979 
980         val resolveRecord = NsdResolveRecord()
981         // Try to resolve an unknown service then stop it immediately.
982         // Expected ResolutionStopped callback.
983         nsdShim.resolveService(nsdManager, si, { it.run() }, resolveRecord)
984         nsdShim.stopServiceResolution(nsdManager, resolveRecord)
985         val stoppedCb = resolveRecord.expectCallback<ResolutionStopped>()
986         assertEquals(si.serviceName, stoppedCb.serviceInfo.serviceName)
987         assertEquals(si.serviceType, stoppedCb.serviceInfo.serviceType)
988     }
989 
990     @Test
991     fun testRegisterServiceInfoCallback() {
992         // This test requires shims supporting U+ APIs (NsdManager.registerServiceInfoCallback)
993         assumeTrue(TestUtils.shouldTestUApis())
994 
995         val lp = cm.getLinkProperties(testNetwork1.network)
996         assertNotNull(lp)
997         val addresses = lp.addresses
998         assertFalse(addresses.isEmpty())
999 
1000         val si = NsdServiceInfo().apply {
1001             serviceType = this@NsdManagerTest.serviceType
1002             serviceName = this@NsdManagerTest.serviceName
1003             network = testNetwork1.network
1004             port = 12345 // Test won't try to connect so port does not matter
1005         }
1006 
1007         // Register service on the network
1008         val registrationRecord = NsdRegistrationRecord()
1009         registerService(registrationRecord, si)
1010 
1011         val discoveryRecord = NsdDiscoveryRecord()
1012         val cbRecord = NsdServiceInfoCallbackRecord()
1013         tryTest {
1014             // Discover service on the network.
1015             nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
1016                     testNetwork1.network, Executor { it.run() }, discoveryRecord)
1017             val foundInfo = discoveryRecord.waitForServiceDiscovered(
1018                     serviceName, serviceType, testNetwork1.network)
1019 
1020             // Register service callback and check the addresses are the same as network addresses
1021             nsdShim.registerServiceInfoCallback(nsdManager, foundInfo, { it.run() }, cbRecord)
1022             val serviceInfoCb = cbRecord.expectCallback<ServiceUpdated>()
1023             assertEquals(foundInfo.serviceName, serviceInfoCb.serviceInfo.serviceName)
1024             val hostAddresses = serviceInfoCb.serviceInfo.hostAddresses
1025             assertEquals(addresses.size, hostAddresses.size)
1026             for (hostAddress in hostAddresses) {
1027                 assertTrue(addresses.contains(hostAddress))
1028             }
1029             checkAddressScopeId(testNetwork1.iface, serviceInfoCb.serviceInfo.hostAddresses)
1030         } cleanupStep {
1031             nsdManager.unregisterService(registrationRecord)
1032             registrationRecord.expectCallback<ServiceUnregistered>()
1033             discoveryRecord.expectCallback<ServiceLost>()
1034             cbRecord.expectCallback<ServiceUpdatedLost>()
1035         } cleanupStep {
1036             // Cancel subscription and check stop callback received.
1037             nsdShim.unregisterServiceInfoCallback(nsdManager, cbRecord)
1038             cbRecord.expectCallback<UnregisterCallbackSucceeded>()
1039         } cleanup {
1040             nsdManager.stopServiceDiscovery(discoveryRecord)
1041             discoveryRecord.expectCallback<DiscoveryStopped>()
1042         }
1043     }
1044 
1045     @Test
1046     fun testStopServiceResolutionFailedCallback() {
1047         // This test requires shims supporting U+ APIs (NsdManager.stopServiceResolution)
1048         assumeTrue(TestUtils.shouldTestUApis())
1049 
1050         // It's not possible to make ResolutionListener#onStopResolutionFailed callback sending
1051         // because it is only sent in very edge-case scenarios when the legacy implementation is
1052         // used, and the legacy implementation is never used in the current AOSP builds. Considering
1053         // that this callback isn't expected to be sent at all at the moment, and this is just an
1054         // interface with no implementation. To verify this callback, just call
1055         // onStopResolutionFailed on the record directly then verify it is received.
1056         val resolveRecord = NsdResolveRecord()
1057         resolveRecord.onStopResolutionFailed(
1058                 NsdServiceInfo(), NsdManager.FAILURE_OPERATION_NOT_RUNNING)
1059         val failedCb = resolveRecord.expectCallback<StopResolutionFailed>()
1060         assertEquals(NsdManager.FAILURE_OPERATION_NOT_RUNNING, failedCb.errorCode)
1061     }
1062 
1063     @Test
1064     fun testSubtypeAdvertisingAndDiscovery() {
1065         val si = makeTestServiceInfo(network = testNetwork1.network)
1066         // Test "_type._tcp.local,_subtype" syntax with the registration
1067         si.serviceType = si.serviceType + ",_subtype"
1068 
1069         val registrationRecord = NsdRegistrationRecord()
1070 
1071         val baseTypeDiscoveryRecord = NsdDiscoveryRecord()
1072         val subtypeDiscoveryRecord = NsdDiscoveryRecord()
1073         val otherSubtypeDiscoveryRecord = NsdDiscoveryRecord()
1074         tryTest {
1075             registerService(registrationRecord, si)
1076 
1077             // Test "_subtype._type._tcp.local" syntax with discovery. Note this is not
1078             // "_subtype._sub._type._tcp.local".
1079             nsdManager.discoverServices(serviceType,
1080                     NsdManager.PROTOCOL_DNS_SD,
1081                     testNetwork1.network, Executor { it.run() }, baseTypeDiscoveryRecord)
1082             nsdManager.discoverServices("_othersubtype.$serviceType",
1083                     NsdManager.PROTOCOL_DNS_SD,
1084                     testNetwork1.network, Executor { it.run() }, otherSubtypeDiscoveryRecord)
1085             nsdManager.discoverServices("_subtype.$serviceType",
1086                     NsdManager.PROTOCOL_DNS_SD,
1087                     testNetwork1.network, Executor { it.run() }, subtypeDiscoveryRecord)
1088 
1089             subtypeDiscoveryRecord.waitForServiceDiscovered(
1090                     serviceName, serviceType, testNetwork1.network)
1091             baseTypeDiscoveryRecord.waitForServiceDiscovered(
1092                     serviceName, serviceType, testNetwork1.network)
1093             otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStarted>()
1094             // The subtype callback was registered later but called, no need for an extra delay
1095             otherSubtypeDiscoveryRecord.assertNoCallback(timeoutMs = 0)
1096         } cleanupStep {
1097             nsdManager.stopServiceDiscovery(baseTypeDiscoveryRecord)
1098             nsdManager.stopServiceDiscovery(subtypeDiscoveryRecord)
1099             nsdManager.stopServiceDiscovery(otherSubtypeDiscoveryRecord)
1100 
1101             baseTypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1102             subtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1103             otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1104         } cleanup {
1105             nsdManager.unregisterService(registrationRecord)
1106         }
1107     }
1108 
1109     /**
1110      * Register a service and return its registration record.
1111      */
1112     private fun registerService(
1113         record: NsdRegistrationRecord,
1114         si: NsdServiceInfo,
1115         executor: Executor = Executor { it.run() }
1116     ): NsdServiceInfo {
1117         nsdShim.registerService(nsdManager, si, NsdManager.PROTOCOL_DNS_SD, executor, record)
1118         // We may not always get the name that we tried to register;
1119         // This events tells us the name that was registered.
1120         val cb = record.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
1121         return cb.serviceInfo
1122     }
1123 
1124     private fun resolveService(discoveredInfo: NsdServiceInfo): NsdServiceInfo {
1125         val record = NsdResolveRecord()
1126         nsdShim.resolveService(nsdManager, discoveredInfo, Executor { it.run() }, record)
1127         val resolvedCb = record.expectCallback<ServiceResolved>()
1128         assertEquals(discoveredInfo.serviceName, resolvedCb.serviceInfo.serviceName)
1129 
1130         return resolvedCb.serviceInfo
1131     }
1132 }
1133 
utf8ToStringnull1134 private fun ByteArray?.utf8ToString(): String {
1135     if (this == null) return ""
1136     return String(this, StandardCharsets.UTF_8)
1137 }
1138