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.Manifest.permission.NETWORK_SETTINGS
20 import android.app.compat.CompatChanges
21 import android.net.ConnectivityManager
22 import android.net.ConnectivityManager.NetworkCallback
23 import android.net.DnsResolver
24 import android.net.InetAddresses.parseNumericAddress
25 import android.net.LinkAddress
26 import android.net.LinkProperties
27 import android.net.LocalSocket
28 import android.net.LocalSocketAddress
29 import android.net.MacAddress
30 import android.net.Network
31 import android.net.NetworkAgentConfig
32 import android.net.NetworkCapabilities
33 import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
34 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
35 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
36 import android.net.NetworkCapabilities.TRANSPORT_TEST
37 import android.net.NetworkRequest
38 import android.net.TestNetworkInterface
39 import android.net.TestNetworkManager
40 import android.net.TestNetworkSpecifier
41 import android.net.connectivity.ConnectivityCompatChanges
42 import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted
43 import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped
44 import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.ServiceFound
45 import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.ServiceLost
46 import android.net.cts.NsdRegistrationRecord.RegistrationEvent.RegistrationFailed
47 import android.net.cts.NsdRegistrationRecord.RegistrationEvent.ServiceRegistered
48 import android.net.cts.NsdRegistrationRecord.RegistrationEvent.ServiceUnregistered
49 import android.net.cts.NsdResolveRecord.ResolveEvent.ResolutionStopped
50 import android.net.cts.NsdResolveRecord.ResolveEvent.ServiceResolved
51 import android.net.cts.NsdResolveRecord.ResolveEvent.StopResolutionFailed
52 import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdated
53 import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdatedLost
54 import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.UnregisterCallbackSucceeded
55 import android.net.cts.util.CtsNetUtils
56 import android.net.nsd.DiscoveryRequest
57 import android.net.nsd.NsdManager
58 import android.net.nsd.NsdServiceInfo
59 import android.net.nsd.OffloadEngine
60 import android.net.nsd.OffloadServiceInfo
61 import android.os.Build
62 import android.os.Handler
63 import android.os.HandlerThread
64 import android.platform.test.annotations.AppModeFull
65 import android.provider.DeviceConfig.NAMESPACE_TETHERING
66 import android.system.ErrnoException
67 import android.system.Os
68 import android.system.OsConstants.AF_INET6
69 import android.system.OsConstants.EADDRNOTAVAIL
70 import android.system.OsConstants.ENETUNREACH
71 import android.system.OsConstants.ETH_P_IPV6
72 import android.system.OsConstants.IPPROTO_IPV6
73 import android.system.OsConstants.IPPROTO_UDP
74 import android.system.OsConstants.RT_SCOPE_LINK
75 import android.system.OsConstants.SOCK_DGRAM
76 import android.util.Log
77 import androidx.test.filters.SmallTest
78 import androidx.test.platform.app.InstrumentationRegistry
79 import com.android.compatibility.common.util.PollingCheck
80 import com.android.compatibility.common.util.PropertyUtil
81 import com.android.compatibility.common.util.SystemUtil
82 import com.android.modules.utils.build.SdkLevel.isAtLeastU
83 import com.android.net.module.util.DnsPacket
84 import com.android.net.module.util.DnsPacket.ANSECTION
85 import com.android.net.module.util.HexDump
86 import com.android.net.module.util.HexDump.hexStringToByteArray
87 import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN
88 import com.android.net.module.util.PacketBuilder
89 import com.android.testutils.ConnectivityModuleTest
90 import com.android.testutils.DevSdkIgnoreRule
91 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
92 import com.android.testutils.DevSdkIgnoreRunner
93 import com.android.testutils.DeviceConfigRule
94 import com.android.testutils.NSResponder
95 import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
96 import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
97 import com.android.testutils.TapPacketReader
98 import com.android.testutils.TestableNetworkAgent
99 import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
100 import com.android.testutils.TestableNetworkCallback
101 import com.android.testutils.assertContainsExactly
102 import com.android.testutils.assertEmpty
103 import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk30
104 import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk33
105 import com.android.testutils.runAsShell
106 import com.android.testutils.tryTest
107 import com.android.testutils.waitForIdle
108 import java.io.File
109 import java.io.IOException
110 import java.net.Inet6Address
111 import java.net.InetAddress
112 import java.net.NetworkInterface
113 import java.net.ServerSocket
114 import java.nio.ByteBuffer
115 import java.nio.charset.StandardCharsets
116 import java.util.Random
117 import java.util.concurrent.Executor
118 import kotlin.math.min
119 import kotlin.test.assertEquals
120 import kotlin.test.assertFailsWith
121 import kotlin.test.assertNotNull
122 import kotlin.test.assertNull
123 import kotlin.test.fail
124 import org.junit.After
125 import org.junit.Assert.assertArrayEquals
126 import org.junit.Assert.assertFalse
127 import org.junit.Assert.assertTrue
128 import org.junit.Assume.assumeTrue
129 import org.junit.Before
130 import org.junit.Rule
131 import org.junit.Test
132 import org.junit.runner.RunWith
133 import kotlin.test.assertNotEquals
134
135 private const val TAG = "NsdManagerTest"
136 private const val TIMEOUT_MS = 2000L
137 // Registration may take a long time if there are devices with the same hostname on the network,
138 // as the device needs to try another name and probe again. This is especially true since when using
139 // mdnsresponder the usual hostname is "Android", and on conflict "Android-2", "Android-3", ... are
140 // tried sequentially
141 private const val REGISTRATION_TIMEOUT_MS = 10_000L
142 private const val DBG = false
143 private const val TEST_PORT = 12345
144 private const val MDNS_PORT = 5353.toShort()
145 private const val TYPE_KEY = 25
146 private const val QCLASS_INTERNET = 0x0001
147 private const val NAME_RECORDS_TTL_MILLIS: Long = 120
148 private val multicastIpv6Addr = parseNumericAddress("ff02::fb") as Inet6Address
149 private val testSrcAddr = parseNumericAddress("2001:db8::123") as Inet6Address
150
151 @AppModeFull(reason = "Socket cannot bind in instant app mode")
152 @RunWith(DevSdkIgnoreRunner::class)
153 @SmallTest
154 @ConnectivityModuleTest
155 @IgnoreUpTo(Build.VERSION_CODES.S_V2)
156 class NsdManagerTest {
157 // Rule used to filter CtsNetTestCasesMaxTargetSdkXX
158 @get:Rule
159 val ignoreRule = DevSdkIgnoreRule()
160
161 @get:Rule
162 val deviceConfigRule = DeviceConfigRule()
163
164 private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
165 private val nsdManager by lazy {
166 context.getSystemService(NsdManager::class.java) ?: fail("Could not get NsdManager service")
167 }
168
169 private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
170 private val serviceName = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
171 private val serviceName2 = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
172 private val serviceName3 = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
173 private val serviceType = "_nmt%09d._tcp".format(Random().nextInt(1_000_000_000))
174 private val serviceType2 = "_nmt%09d._tcp".format(Random().nextInt(1_000_000_000))
175 private val customHostname = "NsdTestHost%09d".format(Random().nextInt(1_000_000_000))
176 private val customHostname2 = "NsdTestHost%09d".format(Random().nextInt(1_000_000_000))
177 private val publicKey = hexStringToByteArray(
178 "0201030dc141d0637960b98cbc12cfca"
179 + "221d2879dac26ee5b460e9007c992e19"
180 + "02d897c391b03764d448f7d0c772fdb0"
181 + "3b1d9d6d52ff8886769e8e2362513565"
182 + "270962d3")
183 private val handlerThread = HandlerThread(NsdManagerTest::class.java.simpleName)
184 private val ctsNetUtils by lazy{ CtsNetUtils(context) }
185
186 private lateinit var testNetwork1: TestTapNetwork
187 private lateinit var testNetwork2: TestTapNetwork
188
189 private class TestTapNetwork(
190 val iface: TestNetworkInterface,
191 val requestCb: NetworkCallback,
192 val agent: TestableNetworkAgent,
193 val network: Network
194 ) {
195 fun close(cm: ConnectivityManager) {
196 cm.unregisterNetworkCallback(requestCb)
197 agent.unregister()
198 iface.fileDescriptor.close()
199 agent.waitForIdle(TIMEOUT_MS)
200 }
201 }
202
203 private class TestNsdOffloadEngine : OffloadEngine,
204 NsdRecord<TestNsdOffloadEngine.OffloadEvent>() {
205 sealed class OffloadEvent : NsdEvent {
206 data class AddOrUpdateEvent(val info: OffloadServiceInfo) : OffloadEvent()
207 data class RemoveEvent(val info: OffloadServiceInfo) : OffloadEvent()
208 }
209
210 override fun onOffloadServiceUpdated(info: OffloadServiceInfo) {
211 add(OffloadEvent.AddOrUpdateEvent(info))
212 }
213
214 override fun onOffloadServiceRemoved(info: OffloadServiceInfo) {
215 add(OffloadEvent.RemoveEvent(info))
216 }
217 }
218
219 @Before
220 fun setUp() {
221 handlerThread.start()
222
223 runAsShell(MANAGE_TEST_NETWORKS) {
224 testNetwork1 = createTestNetwork()
225 testNetwork2 = createTestNetwork()
226 }
227 }
228
229 private fun createTestNetwork(): TestTapNetwork {
230 val tnm = context.getSystemService(TestNetworkManager::class.java)!!
231 val iface = tnm.createTapInterface()
232 val cb = TestableNetworkCallback()
233 val testNetworkSpecifier = TestNetworkSpecifier(iface.interfaceName)
234 cm.requestNetwork(NetworkRequest.Builder()
235 .removeCapability(NET_CAPABILITY_TRUSTED)
236 .addTransportType(TRANSPORT_TEST)
237 .setNetworkSpecifier(testNetworkSpecifier)
238 .build(), cb)
239 val agent = registerTestNetworkAgent(iface.interfaceName)
240 val network = agent.network ?: fail("Registered agent should have a network")
241
242 cb.eventuallyExpect<LinkPropertiesChanged>(TIMEOUT_MS) {
243 it.lp.linkAddresses.isNotEmpty()
244 }
245
246 // The network has no INTERNET capability, so will be marked validated immediately
247 // It does not matter if validated capabilities come before/after the link addresses change
248 cb.eventuallyExpect<CapabilitiesChanged>(TIMEOUT_MS, from = 0) {
249 it.caps.hasCapability(NET_CAPABILITY_VALIDATED)
250 }
251 return TestTapNetwork(iface, cb, agent, network)
252 }
253
254 private fun registerTestNetworkAgent(ifaceName: String): TestableNetworkAgent {
255 val lp = LinkProperties().apply {
256 interfaceName = ifaceName
257 }
258 val agent = TestableNetworkAgent(context, handlerThread.looper,
259 NetworkCapabilities().apply {
260 removeCapability(NET_CAPABILITY_TRUSTED)
261 addTransportType(TRANSPORT_TEST)
262 setNetworkSpecifier(TestNetworkSpecifier(ifaceName))
263 }, lp, NetworkAgentConfig.Builder().build())
264 val network = agent.register()
265 agent.markConnected()
266 agent.expectCallback<OnNetworkCreated>()
267
268 // Wait until the link-local address can be used. Address flags are not available without
269 // elevated permissions, so check that bindSocket works.
270 PollingCheck.check("No usable v6 address on interface after $TIMEOUT_MS ms", TIMEOUT_MS) {
271 // To avoid race condition between socket connection succeeding and interface returning
272 // a non-empty address list. Verify that interface returns a non-empty list, before
273 // trying the socket connection.
274 if (NetworkInterface.getByName(ifaceName).interfaceAddresses.isEmpty()) {
275 return@check false
276 }
277
278 val sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
279 tryTest {
280 network.bindSocket(sock)
281 Os.connect(sock, parseNumericAddress("ff02::fb%$ifaceName"), 12345)
282 true
283 }.catch<ErrnoException> {
284 if (it.errno != ENETUNREACH && it.errno != EADDRNOTAVAIL) {
285 throw it
286 }
287 false
288 } cleanup {
289 Os.close(sock)
290 }
291 }
292
293 lp.setLinkAddresses(NetworkInterface.getByName(ifaceName).interfaceAddresses.map {
294 LinkAddress(it.address, it.networkPrefixLength.toInt())
295 })
296 agent.sendLinkProperties(lp)
297 return agent
298 }
299
300 private fun makeTestServiceInfo(network: Network? = null) = NsdServiceInfo().also {
301 it.serviceType = serviceType
302 it.serviceName = serviceName
303 it.network = network
304 it.port = TEST_PORT
305 }
306
307 @After
308 fun tearDown() {
309 runAsShell(MANAGE_TEST_NETWORKS) {
310 // Avoid throwing here if initializing failed in setUp
311 if (this::testNetwork1.isInitialized) testNetwork1.close(cm)
312 if (this::testNetwork2.isInitialized) testNetwork2.close(cm)
313 }
314 handlerThread.waitForIdle(TIMEOUT_MS)
315 handlerThread.quitSafely()
316 handlerThread.join()
317 }
318
319 @Test
320 fun testNsdManager() {
321 val si = NsdServiceInfo()
322 si.serviceType = serviceType
323 si.serviceName = serviceName
324 // Test binary data with various bytes
325 val testByteArray = byteArrayOf(-128, 127, 2, 1, 0, 1, 2)
326 // Test string data with 256 characters (25 blocks of 10 characters + 6)
327 val string256 = "1_________2_________3_________4_________5_________6_________" +
328 "7_________8_________9_________10________11________12________13________" +
329 "14________15________16________17________18________19________20________" +
330 "21________22________23________24________25________123456"
331
332 // Illegal attributes
333 listOf(
334 Triple(null, null, "null key"),
335 Triple("", null, "empty key"),
336 Triple(string256, null, "key with 256 characters"),
337 Triple("key", string256.substring(3),
338 "key+value combination with more than 255 characters"),
339 Triple("key", string256.substring(4), "key+value combination with 255 characters"),
340 Triple("\u0019", null, "key with invalid character"),
341 Triple("=", null, "key with invalid character"),
342 Triple("\u007f", null, "key with invalid character")
343 ).forEach {
344 assertFailsWith<IllegalArgumentException>(
345 "Setting invalid ${it.third} unexpectedly succeeded") {
346 si.setAttribute(it.first, it.second)
347 }
348 }
349
350 // Allowed attributes
351 si.setAttribute("booleanAttr", null as String?)
352 si.setAttribute("keyValueAttr", "value")
353 si.setAttribute("keyEqualsAttr", "=")
354 si.setAttribute(" whiteSpaceKeyValueAttr ", " value ")
355 si.setAttribute("binaryDataAttr", testByteArray)
356 si.setAttribute("nullBinaryDataAttr", null as ByteArray?)
357 si.setAttribute("emptyBinaryDataAttr", byteArrayOf())
358 si.setAttribute("longkey", string256.substring(9))
359 val socket = ServerSocket(0)
360 val localPort = socket.localPort
361 si.port = localPort
362 if (DBG) Log.d(TAG, "Port = $localPort")
363
364 val registrationRecord = NsdRegistrationRecord()
365 // Test registering without an Executor
366 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, registrationRecord)
367 val registeredInfo = registrationRecord.expectCallback<ServiceRegistered>(
368 REGISTRATION_TIMEOUT_MS).serviceInfo
369
370 // Only service name is included in ServiceRegistered callbacks
371 assertNull(registeredInfo.serviceType)
372 assertEquals(si.serviceName, registeredInfo.serviceName)
373
374 val discoveryRecord = NsdDiscoveryRecord()
375 // Test discovering without an Executor
376 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
377
378 // Expect discovery started
379 discoveryRecord.expectCallback<DiscoveryStarted>()
380
381 // Expect a service record to be discovered
382 val foundInfo = discoveryRecord.waitForServiceDiscovered(
383 registeredInfo.serviceName, serviceType)
384
385 // Test resolving without an Executor
386 val resolveRecord = NsdResolveRecord()
387 nsdManager.resolveService(foundInfo, resolveRecord)
388 val resolvedService = resolveRecord.expectCallback<ServiceResolved>().serviceInfo
389 assertEquals(".$serviceType", resolvedService.serviceType)
390 assertEquals(registeredInfo.serviceName, resolvedService.serviceName)
391
392 // Check Txt attributes
393 assertEquals(8, resolvedService.attributes.size)
394 assertTrue(resolvedService.attributes.containsKey("booleanAttr"))
395 assertNull(resolvedService.attributes["booleanAttr"])
396 assertEquals("value", resolvedService.attributes["keyValueAttr"].utf8ToString())
397 assertEquals("=", resolvedService.attributes["keyEqualsAttr"].utf8ToString())
398 assertEquals(" value ",
399 resolvedService.attributes[" whiteSpaceKeyValueAttr "].utf8ToString())
400 assertEquals(string256.substring(9), resolvedService.attributes["longkey"].utf8ToString())
401 assertArrayEquals(testByteArray, resolvedService.attributes["binaryDataAttr"])
402 assertTrue(resolvedService.attributes.containsKey("nullBinaryDataAttr"))
403 assertNull(resolvedService.attributes["nullBinaryDataAttr"])
404 assertTrue(resolvedService.attributes.containsKey("emptyBinaryDataAttr"))
405 if (isAtLeastU() || CompatChanges.isChangeEnabled(
406 ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND
407 )) {
408 assertArrayEquals(byteArrayOf(), resolvedService.attributes["emptyBinaryDataAttr"])
409 } else {
410 assertNull(resolvedService.attributes["emptyBinaryDataAttr"])
411 }
412 assertEquals(localPort, resolvedService.port)
413
414 // Unregister the service
415 nsdManager.unregisterService(registrationRecord)
416 registrationRecord.expectCallback<ServiceUnregistered>()
417
418 // Expect a callback for service lost
419 val lostCb = discoveryRecord.expectCallbackEventually<ServiceLost> {
420 it.serviceInfo.serviceName == serviceName
421 }
422 // Lost service types have a dot at the end
423 assertEquals("$serviceType.", lostCb.serviceInfo.serviceType)
424
425 // Register service again to see if NsdManager can discover it
426 val si2 = NsdServiceInfo()
427 si2.serviceType = serviceType
428 si2.serviceName = serviceName
429 si2.port = localPort
430 val registrationRecord2 = NsdRegistrationRecord()
431 nsdManager.registerService(si2, NsdManager.PROTOCOL_DNS_SD, registrationRecord2)
432 val registeredInfo2 = registrationRecord2.expectCallback<ServiceRegistered>(
433 REGISTRATION_TIMEOUT_MS).serviceInfo
434
435 // Expect a service record to be discovered (and filter the ones
436 // that are unrelated to this test)
437 val foundInfo2 = discoveryRecord.waitForServiceDiscovered(
438 registeredInfo2.serviceName, serviceType)
439
440 // Resolve the service
441 val resolveRecord2 = NsdResolveRecord()
442 nsdManager.resolveService(foundInfo2, resolveRecord2)
443 val resolvedService2 = resolveRecord2.expectCallback<ServiceResolved>().serviceInfo
444
445 // Check that the resolved service doesn't have any TXT records
446 assertEquals(0, resolvedService2.attributes.size)
447
448 nsdManager.stopServiceDiscovery(discoveryRecord)
449
450 discoveryRecord.expectCallbackEventually<DiscoveryStopped>()
451
452 nsdManager.unregisterService(registrationRecord2)
453 registrationRecord2.expectCallback<ServiceUnregistered>()
454 }
455
456 @Test
457 fun testNsdManager_DiscoverOnNetwork() {
458 val si = makeTestServiceInfo()
459 val registrationRecord = NsdRegistrationRecord()
460 val registeredInfo = registerService(registrationRecord, si)
461
462 tryTest {
463 val discoveryRecord = NsdDiscoveryRecord()
464 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
465 testNetwork1.network, Executor { it.run() }, discoveryRecord)
466
467 val foundInfo = discoveryRecord.waitForServiceDiscovered(
468 serviceName, serviceType, testNetwork1.network)
469 assertEquals(testNetwork1.network, foundInfo.network)
470
471 // Rewind to ensure the service is not found on the other interface
472 discoveryRecord.nextEvents.rewind(0)
473 assertNull(discoveryRecord.nextEvents.poll(timeoutMs = 100L) {
474 it is ServiceFound &&
475 it.serviceInfo.serviceName == registeredInfo.serviceName &&
476 it.serviceInfo.network != testNetwork1.network
477 }, "The service should not be found on this network")
478 } cleanup {
479 nsdManager.unregisterService(registrationRecord)
480 }
481 }
482
483 @Test
484 fun testNsdManager_DiscoverWithNetworkRequest() {
485 val si = makeTestServiceInfo()
486 val handler = Handler(handlerThread.looper)
487 val executor = Executor { handler.post(it) }
488
489 val registrationRecord = NsdRegistrationRecord(expectedThreadId = handlerThread.threadId)
490 val registeredInfo1 = registerService(registrationRecord, si, executor)
491 val discoveryRecord = NsdDiscoveryRecord(expectedThreadId = handlerThread.threadId)
492
493 tryTest {
494 val specifier = TestNetworkSpecifier(testNetwork1.iface.interfaceName)
495 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
496 NetworkRequest.Builder()
497 .removeCapability(NET_CAPABILITY_TRUSTED)
498 .addTransportType(TRANSPORT_TEST)
499 .setNetworkSpecifier(specifier)
500 .build(),
501 executor, discoveryRecord)
502
503 val discoveryStarted = discoveryRecord.expectCallback<DiscoveryStarted>()
504 assertEquals(serviceType, discoveryStarted.serviceType)
505
506 val serviceDiscovered = discoveryRecord.expectCallback<ServiceFound>()
507 assertEquals(registeredInfo1.serviceName, serviceDiscovered.serviceInfo.serviceName)
508 // Discovered service types have a dot at the end
509 assertEquals("$serviceType.", serviceDiscovered.serviceInfo.serviceType)
510 assertEquals(testNetwork1.network, serviceDiscovered.serviceInfo.network)
511
512 // Unregister, then register the service back: it should be lost and found again
513 nsdManager.unregisterService(registrationRecord)
514 val serviceLost1 = discoveryRecord.expectCallback<ServiceLost>()
515 assertEquals(registeredInfo1.serviceName, serviceLost1.serviceInfo.serviceName)
516 assertEquals(testNetwork1.network, serviceLost1.serviceInfo.network)
517
518 registrationRecord.expectCallback<ServiceUnregistered>()
519 val registeredInfo2 = registerService(registrationRecord, si, executor)
520 val serviceDiscovered2 = discoveryRecord.expectCallback<ServiceFound>()
521 assertEquals(registeredInfo2.serviceName, serviceDiscovered2.serviceInfo.serviceName)
522 assertEquals("$serviceType.", serviceDiscovered2.serviceInfo.serviceType)
523 assertEquals(testNetwork1.network, serviceDiscovered2.serviceInfo.network)
524
525 // Teardown, then bring back up a network on the test interface: the service should
526 // go away, then come back
527 testNetwork1.agent.unregister()
528 val serviceLost = discoveryRecord.expectCallback<ServiceLost>()
529 assertEquals(registeredInfo2.serviceName, serviceLost.serviceInfo.serviceName)
530 assertEquals(testNetwork1.network, serviceLost.serviceInfo.network)
531
532 val newAgent = runAsShell(MANAGE_TEST_NETWORKS) {
533 registerTestNetworkAgent(testNetwork1.iface.interfaceName)
534 }
535 val newNetwork = newAgent.network ?: fail("Registered agent should have a network")
536 val serviceDiscovered3 = discoveryRecord.expectCallback<ServiceFound>()
537 assertEquals(registeredInfo2.serviceName, serviceDiscovered3.serviceInfo.serviceName)
538 assertEquals("$serviceType.", serviceDiscovered3.serviceInfo.serviceType)
539 assertEquals(newNetwork, serviceDiscovered3.serviceInfo.network)
540 } cleanupStep {
541 nsdManager.stopServiceDiscovery(discoveryRecord)
542 discoveryRecord.expectCallback<DiscoveryStopped>()
543 } cleanup {
544 nsdManager.unregisterService(registrationRecord)
545 }
546 }
547
548 @Test
549 fun testNsdManager_DiscoverWithNetworkRequest_NoMatchingNetwork() {
550 val handler = Handler(handlerThread.looper)
551 val executor = Executor { handler.post(it) }
552
553 val discoveryRecord = NsdDiscoveryRecord(expectedThreadId = handlerThread.threadId)
554 val specifier = TestNetworkSpecifier(testNetwork1.iface.interfaceName)
555
556 tryTest {
557 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
558 NetworkRequest.Builder()
559 .removeCapability(NET_CAPABILITY_TRUSTED)
560 .addTransportType(TRANSPORT_TEST)
561 // Specified network does not have this capability
562 .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)
563 .setNetworkSpecifier(specifier)
564 .build(),
565 executor, discoveryRecord)
566 discoveryRecord.expectCallback<DiscoveryStarted>()
567 } cleanup {
568 nsdManager.stopServiceDiscovery(discoveryRecord)
569 discoveryRecord.expectCallback<DiscoveryStopped>()
570 }
571 }
572
573 private fun checkAddressScopeId(iface: TestNetworkInterface, address: List<InetAddress>) {
574 val targetSdkVersion = context.packageManager
575 .getTargetSdkVersion(context.applicationInfo.packageName)
576 if (targetSdkVersion <= Build.VERSION_CODES.TIRAMISU) {
577 return
578 }
579 val ifaceIdx = NetworkInterface.getByName(iface.interfaceName).index
580 address.forEach {
581 if (it is Inet6Address && it.isLinkLocalAddress) {
582 assertEquals(ifaceIdx, it.scopeId)
583 }
584 }
585 }
586
587 @Test
588 fun testNsdManager_ResolveOnNetwork() {
589 val si = makeTestServiceInfo()
590 val registrationRecord = NsdRegistrationRecord()
591 val registeredInfo = registerService(registrationRecord, si)
592 tryTest {
593 val resolveRecord = NsdResolveRecord()
594
595 val discoveryRecord = NsdDiscoveryRecord()
596 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
597
598 val foundInfo1 = discoveryRecord.waitForServiceDiscovered(
599 serviceName, serviceType, testNetwork1.network)
600 assertEquals(testNetwork1.network, foundInfo1.network)
601 // Rewind as the service could be found on each interface in any order
602 discoveryRecord.nextEvents.rewind(0)
603 val foundInfo2 = discoveryRecord.waitForServiceDiscovered(
604 serviceName, serviceType, testNetwork2.network)
605 assertEquals(testNetwork2.network, foundInfo2.network)
606
607 nsdManager.resolveService(foundInfo1, Executor { it.run() }, resolveRecord)
608 val cb = resolveRecord.expectCallback<ServiceResolved>()
609 cb.serviceInfo.let {
610 // Resolved service type has leading dot
611 assertEquals(".$serviceType", it.serviceType)
612 assertEquals(registeredInfo.serviceName, it.serviceName)
613 assertEquals(si.port, it.port)
614 assertEquals(testNetwork1.network, it.network)
615 checkAddressScopeId(testNetwork1.iface, it.hostAddresses)
616 }
617 // TODO: check that MDNS packets are sent only on testNetwork1.
618 } cleanupStep {
619 nsdManager.unregisterService(registrationRecord)
620 } cleanup {
621 registrationRecord.expectCallback<ServiceUnregistered>()
622 }
623 }
624
625 @Test
626 fun testNsdManager_RegisterOnNetwork() {
627 val si = makeTestServiceInfo(testNetwork1.network)
628 // Register service on testNetwork1
629 val registrationRecord = NsdRegistrationRecord()
630 registerService(registrationRecord, si)
631 val discoveryRecord = NsdDiscoveryRecord()
632 val discoveryRecord2 = NsdDiscoveryRecord()
633 val discoveryRecord3 = NsdDiscoveryRecord()
634
635 tryTest {
636 // Discover service on testNetwork1.
637 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
638 testNetwork1.network, Executor { it.run() }, discoveryRecord)
639 // Expect that service is found on testNetwork1
640 val foundInfo = discoveryRecord.waitForServiceDiscovered(
641 serviceName, serviceType, testNetwork1.network)
642 assertEquals(testNetwork1.network, foundInfo.network)
643
644 // Discover service on testNetwork2.
645 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
646 testNetwork2.network, Executor { it.run() }, discoveryRecord2)
647 // Expect that discovery is started then no other callbacks.
648 discoveryRecord2.expectCallback<DiscoveryStarted>()
649 discoveryRecord2.assertNoCallback()
650
651 // Discover service on all networks (not specify any network).
652 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
653 null as Network? /* network */, Executor { it.run() }, discoveryRecord3)
654 // Expect that service is found on testNetwork1
655 val foundInfo3 = discoveryRecord3.waitForServiceDiscovered(
656 serviceName, serviceType, testNetwork1.network)
657 assertEquals(testNetwork1.network, foundInfo3.network)
658 } cleanupStep {
659 nsdManager.stopServiceDiscovery(discoveryRecord2)
660 discoveryRecord2.expectCallback<DiscoveryStopped>()
661 } cleanup {
662 nsdManager.unregisterService(registrationRecord)
663 }
664 }
665
666 @Test
667 fun testNsdManager_RegisterServiceNameWithNonStandardCharacters() {
668 val serviceNames = "^Nsd.Test|Non-#AsCiI\\Characters&\\ufffe テスト 測試"
669 val si = NsdServiceInfo().apply {
670 serviceType = this@NsdManagerTest.serviceType
671 serviceName = serviceNames
672 port = 12345 // Test won't try to connect so port does not matter
673 }
674
675 // Register the service name which contains non-standard characters.
676 val registrationRecord = NsdRegistrationRecord()
677 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, registrationRecord)
678 registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
679
680 tryTest {
681 // Discover that service name.
682 val discoveryRecord = NsdDiscoveryRecord()
683 nsdManager.discoverServices(
684 serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord
685 )
686 val foundInfo = discoveryRecord.waitForServiceDiscovered(serviceNames, serviceType)
687
688 // Expect that resolving the service name works properly even service name contains
689 // non-standard characters.
690 val resolveRecord = NsdResolveRecord()
691 nsdManager.resolveService(foundInfo, resolveRecord)
692 val resolvedCb = resolveRecord.expectCallback<ServiceResolved>()
693 assertEquals(foundInfo.serviceName, resolvedCb.serviceInfo.serviceName)
694 } cleanupStep {
695 nsdManager.unregisterService(registrationRecord)
696 } cleanup {
697 registrationRecord.expectCallback<ServiceUnregistered>()
698 }
699 }
700
701 @Test
702 fun testRegisterService_twoServicesWithSameNameButDifferentTypes_registeredAndDiscoverable() {
703 val si1 = NsdServiceInfo().also {
704 it.network = testNetwork1.network
705 it.serviceName = serviceName
706 it.serviceType = serviceType
707 it.port = TEST_PORT
708 }
709 val si2 = NsdServiceInfo().also {
710 it.network = testNetwork1.network
711 it.serviceName = serviceName
712 it.serviceType = serviceType2
713 it.port = TEST_PORT + 1
714 }
715 val registrationRecord1 = NsdRegistrationRecord()
716 val registrationRecord2 = NsdRegistrationRecord()
717 val discoveryRecord1 = NsdDiscoveryRecord()
718 val discoveryRecord2 = NsdDiscoveryRecord()
719 tryTest {
720 registerService(registrationRecord1, si1)
721 registerService(registrationRecord2, si2)
722
723 nsdManager.discoverServices(serviceType,
724 NsdManager.PROTOCOL_DNS_SD,
725 testNetwork1.network, Executor { it.run() }, discoveryRecord1)
726 nsdManager.discoverServices(serviceType2,
727 NsdManager.PROTOCOL_DNS_SD,
728 testNetwork1.network, Executor { it.run() }, discoveryRecord2)
729
730 discoveryRecord1.waitForServiceDiscovered(serviceName, serviceType,
731 testNetwork1.network)
732 discoveryRecord2.waitForServiceDiscovered(serviceName, serviceType2,
733 testNetwork1.network)
734 } cleanupStep {
735 nsdManager.stopServiceDiscovery(discoveryRecord1)
736 nsdManager.stopServiceDiscovery(discoveryRecord2)
737 } cleanup {
738 nsdManager.unregisterService(registrationRecord1)
739 nsdManager.unregisterService(registrationRecord2)
740 }
741 }
742
743 fun checkOffloadServiceInfo(serviceInfo: OffloadServiceInfo, si: NsdServiceInfo) {
744 val expectedServiceType = si.serviceType.split(",")[0]
745 assertEquals(si.serviceName, serviceInfo.key.serviceName)
746 assertEquals(expectedServiceType, serviceInfo.key.serviceType)
747 assertEquals(listOf("_subtype"), serviceInfo.subtypes)
748 assertTrue(serviceInfo.hostname.startsWith("Android_"))
749 assertTrue(serviceInfo.hostname.endsWith("local"))
750 // Test service types should not be in the priority list
751 assertEquals(Integer.MAX_VALUE, serviceInfo.priority)
752 assertEquals(OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(), serviceInfo.offloadType)
753 val offloadPayload = serviceInfo.offloadPayload
754 assertNotNull(offloadPayload)
755 val dnsPacket = TestDnsPacket(offloadPayload, dstAddr = multicastIpv6Addr)
756 assertEquals(0x8400, dnsPacket.header.flags)
757 assertEquals(0, dnsPacket.records[DnsPacket.QDSECTION].size)
758 assertTrue(dnsPacket.records[DnsPacket.ANSECTION].size >= 5)
759 assertEquals(0, dnsPacket.records[DnsPacket.NSSECTION].size)
760 assertEquals(0, dnsPacket.records[DnsPacket.ARSECTION].size)
761
762 val ptrRecord = dnsPacket.records[DnsPacket.ANSECTION][0]
763 assertEquals("$expectedServiceType.local", ptrRecord.dName)
764 assertEquals(0x0C /* PTR */, ptrRecord.nsType)
765 val ptrSubRecord = dnsPacket.records[DnsPacket.ANSECTION][1]
766 assertEquals("_subtype._sub.$expectedServiceType.local", ptrSubRecord.dName)
767 assertEquals(0x0C /* PTR */, ptrSubRecord.nsType)
768 val srvRecord = dnsPacket.records[DnsPacket.ANSECTION][2]
769 assertEquals("${si.serviceName}.$expectedServiceType.local", srvRecord.dName)
770 assertEquals(0x21 /* SRV */, srvRecord.nsType)
771 val txtRecord = dnsPacket.records[DnsPacket.ANSECTION][3]
772 assertEquals("${si.serviceName}.$expectedServiceType.local", txtRecord.dName)
773 assertEquals(0x10 /* TXT */, txtRecord.nsType)
774 val iface = NetworkInterface.getByName(testNetwork1.iface.interfaceName)
775 val allAddress = iface.inetAddresses.toList()
776 for (i in 4 until dnsPacket.records[DnsPacket.ANSECTION].size) {
777 val addressRecord = dnsPacket.records[DnsPacket.ANSECTION][i]
778 assertTrue(addressRecord.dName.startsWith("Android_"))
779 assertTrue(addressRecord.dName.endsWith("local"))
780 assertTrue(addressRecord.nsType in arrayOf(0x1C /* AAAA */, 0x01 /* A */))
781 val rData = addressRecord.rr
782 assertNotNull(rData)
783 val addr = InetAddress.getByAddress(rData)
784 assertTrue(addr in allAddress)
785 }
786 }
787
788 @Test
789 fun testNsdManager_registerOffloadEngine() {
790 val targetSdkVersion = context.packageManager
791 .getTargetSdkVersion(context.applicationInfo.packageName)
792 // The offload callbacks are only supported with the new backend,
793 // enabled with target SDK U+.
794 assumeTrue(isAtLeastU() || targetSdkVersion > Build.VERSION_CODES.TIRAMISU)
795
796 // TODO: also have a test that use an executor that runs in a different thread, and pass
797 // in the thread ID NsdServiceInfo to check it
798 val si1 = NsdServiceInfo()
799 si1.serviceType = "$serviceType,_subtype"
800 si1.serviceName = serviceName
801 si1.network = testNetwork1.network
802 si1.port = 23456
803 val record1 = NsdRegistrationRecord()
804
805 val si2 = NsdServiceInfo()
806 si2.serviceType = "$serviceType,_subtype"
807 si2.serviceName = serviceName2
808 si2.network = testNetwork1.network
809 si2.port = 12345
810 val record2 = NsdRegistrationRecord()
811 val offloadEngine = TestNsdOffloadEngine()
812
813 tryTest {
814 // Register service before the OffloadEngine is registered.
815 nsdManager.registerService(si1, NsdManager.PROTOCOL_DNS_SD, record1)
816 record1.expectCallback<ServiceRegistered>()
817 runAsShell(NETWORK_SETTINGS) {
818 nsdManager.registerOffloadEngine(testNetwork1.iface.interfaceName,
819 OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(),
820 OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK.toLong(),
821 { it.run() }, offloadEngine)
822 }
823 val addOrUpdateEvent1 = offloadEngine
824 .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.AddOrUpdateEvent> {
825 it.info.key.serviceName == si1.serviceName
826 }
827 checkOffloadServiceInfo(addOrUpdateEvent1.info, si1)
828
829 // Register service after OffloadEngine is registered.
830 nsdManager.registerService(si2, NsdManager.PROTOCOL_DNS_SD, record2)
831 record2.expectCallback<ServiceRegistered>()
832 val addOrUpdateEvent2 = offloadEngine
833 .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.AddOrUpdateEvent> {
834 it.info.key.serviceName == si2.serviceName
835 }
836 checkOffloadServiceInfo(addOrUpdateEvent2.info, si2)
837
838 nsdManager.unregisterService(record2)
839 record2.expectCallback<ServiceUnregistered>()
840 val unregisterEvent = offloadEngine
841 .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.RemoveEvent> {
842 it.info.key.serviceName == si2.serviceName
843 }
844 checkOffloadServiceInfo(unregisterEvent.info, si2)
845 } cleanupStep {
846 runAsShell(NETWORK_SETTINGS) {
847 nsdManager.unregisterOffloadEngine(offloadEngine)
848 }
849 } cleanup {
850 nsdManager.unregisterService(record1)
851 record1.expectCallback<ServiceUnregistered>()
852 }
853 }
854
855 private fun checkConnectSocketToMdnsd(shouldFail: Boolean) {
856 val discoveryRecord = NsdDiscoveryRecord()
857 val localSocket = LocalSocket()
858 tryTest {
859 // Discover any service from NsdManager to enforce NsdService to start the mdnsd.
860 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
861 discoveryRecord.expectCallback<DiscoveryStarted>()
862
863 // Checks the /dev/socket/mdnsd is created.
864 val socket = File("/dev/socket/mdnsd")
865 val doesSocketExist = PollingCheck.waitFor(
866 TIMEOUT_MS,
867 {
868 socket.exists()
869 },
870 { doesSocketExist ->
871 doesSocketExist
872 },
873 )
874
875 // If the socket is not created, then no need to check the access.
876 if (doesSocketExist) {
877 // Create a LocalSocket and try to connect to mdnsd.
878 assertFalse("LocalSocket is connected.", localSocket.isConnected)
879 val address = LocalSocketAddress("mdnsd", LocalSocketAddress.Namespace.RESERVED)
880 if (shouldFail) {
881 assertFailsWith<IOException>("Expect fail but socket connected") {
882 localSocket.connect(address)
883 }
884 } else {
885 localSocket.connect(address)
886 assertTrue("LocalSocket is not connected.", localSocket.isConnected)
887 }
888 }
889 } cleanup {
890 localSocket.close()
891 nsdManager.stopServiceDiscovery(discoveryRecord)
892 discoveryRecord.expectCallback<DiscoveryStopped>()
893 }
894 }
895
896 /**
897 * Starting from Android U, the access to the /dev/socket/mdnsd is blocked by the
898 * sepolicy(b/265364111).
899 */
900 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
901 @Test
902 fun testCannotConnectSocketToMdnsd() {
903 val targetSdkVersion = context.packageManager
904 .getTargetSdkVersion(context.applicationInfo.packageName)
905 assumeTrue(targetSdkVersion > Build.VERSION_CODES.TIRAMISU)
906 val firstApiLevel = min(PropertyUtil.getFirstApiLevel(), PropertyUtil.getVendorApiLevel())
907 // The sepolicy is implemented in the vendor image, so the access may not be blocked if
908 // the vendor image is not update to date.
909 assumeTrue(firstApiLevel > Build.VERSION_CODES.TIRAMISU)
910 checkConnectSocketToMdnsd(shouldFail = true)
911 }
912
913 @Test @CtsNetTestCasesMaxTargetSdk33("mdnsd socket is accessible up to target SDK 33")
914 fun testCanConnectSocketToMdnsd() {
915 checkConnectSocketToMdnsd(shouldFail = false)
916 }
917
918 // Native mdns powered by Netd is removed after U.
919 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
920 @Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
921 fun testManagerCreatesLegacySocket() {
922 nsdManager // Ensure the lazy-init member is initialized, so NsdManager is created
923 val socket = File("/dev/socket/mdnsd")
924 val timeout = System.currentTimeMillis() + TIMEOUT_MS
925 while (!socket.exists() && System.currentTimeMillis() < timeout) {
926 Thread.sleep(10)
927 }
928 assertTrue("$socket was not found after $TIMEOUT_MS ms", socket.exists())
929 }
930
931 // The compat change is part of a connectivity module update that applies to T+
932 @ConnectivityModuleTest @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
933 @Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
934 fun testManagerCreatesLegacySocket_CompatChange() {
935 // The socket may have been already created by some other app, or some other test, in which
936 // case this test cannot verify creation. At least verify that the compat change is
937 // disabled in a process with max SDK 30; unit tests already verify that start is requested
938 // when the compat change is disabled.
939 // Note that before T the compat constant had a different int value.
940 assertFalse(CompatChanges.isChangeEnabled(
941 ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER))
942 }
943
944 @Test
945 fun testStopServiceResolution() {
946 val si = makeTestServiceInfo()
947 val resolveRecord = NsdResolveRecord()
948 // Try to resolve an unknown service then stop it immediately.
949 // Expected ResolutionStopped callback.
950 nsdManager.resolveService(si, { it.run() }, resolveRecord)
951 nsdManager.stopServiceResolution(resolveRecord)
952 val stoppedCb = resolveRecord.expectCallback<ResolutionStopped>()
953 assertEquals(si.serviceName, stoppedCb.serviceInfo.serviceName)
954 assertEquals(si.serviceType, stoppedCb.serviceInfo.serviceType)
955 }
956
957 @Test
958 fun testRegisterServiceInfoCallback() {
959 val lp = cm.getLinkProperties(testNetwork1.network)
960 assertNotNull(lp)
961 val addresses = lp.addresses
962 assertFalse(addresses.isEmpty())
963
964 val si = makeTestServiceInfo(testNetwork1.network)
965
966 // Register service on the network
967 val registrationRecord = NsdRegistrationRecord()
968 registerService(registrationRecord, si)
969
970 val discoveryRecord = NsdDiscoveryRecord()
971 val cbRecord = NsdServiceInfoCallbackRecord()
972 tryTest {
973 // Discover service on the network.
974 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
975 testNetwork1.network, Executor { it.run() }, discoveryRecord)
976 val foundInfo = discoveryRecord.waitForServiceDiscovered(
977 serviceName, serviceType, testNetwork1.network)
978
979 // Register service callback and check the addresses are the same as network addresses
980 nsdManager.registerServiceInfoCallback(foundInfo, { it.run() }, cbRecord)
981 val serviceInfoCb = cbRecord.expectCallback<ServiceUpdated>()
982 assertEquals(foundInfo.serviceName, serviceInfoCb.serviceInfo.serviceName)
983 val hostAddresses = serviceInfoCb.serviceInfo.hostAddresses
984 assertEquals(addresses.size, hostAddresses.size)
985 for (hostAddress in hostAddresses) {
986 assertTrue(addresses.contains(hostAddress))
987 }
988 checkAddressScopeId(testNetwork1.iface, serviceInfoCb.serviceInfo.hostAddresses)
989 } cleanupStep {
990 nsdManager.unregisterService(registrationRecord)
991 registrationRecord.expectCallback<ServiceUnregistered>()
992 discoveryRecord.expectCallback<ServiceLost>()
993 cbRecord.expectCallback<ServiceUpdatedLost>()
994 } cleanupStep {
995 // Cancel subscription and check stop callback received.
996 nsdManager.unregisterServiceInfoCallback(cbRecord)
997 cbRecord.expectCallback<UnregisterCallbackSucceeded>()
998 } cleanup {
999 nsdManager.stopServiceDiscovery(discoveryRecord)
1000 discoveryRecord.expectCallback<DiscoveryStopped>()
1001 }
1002 }
1003
1004 @Test
1005 fun testStopServiceResolutionFailedCallback() {
1006 // It's not possible to make ResolutionListener#onStopResolutionFailed callback sending
1007 // because it is only sent in very edge-case scenarios when the legacy implementation is
1008 // used, and the legacy implementation is never used in the current AOSP builds. Considering
1009 // that this callback isn't expected to be sent at all at the moment, and this is just an
1010 // interface with no implementation. To verify this callback, just call
1011 // onStopResolutionFailed on the record directly then verify it is received.
1012 val resolveRecord = NsdResolveRecord()
1013 resolveRecord.onStopResolutionFailed(
1014 NsdServiceInfo(), NsdManager.FAILURE_OPERATION_NOT_RUNNING)
1015 val failedCb = resolveRecord.expectCallback<StopResolutionFailed>()
1016 assertEquals(NsdManager.FAILURE_OPERATION_NOT_RUNNING, failedCb.errorCode)
1017 }
1018
1019 @Test
1020 fun testSubtypeAdvertisingAndDiscovery() {
1021 val si = makeTestServiceInfo(network = testNetwork1.network)
1022 // Test "_type._tcp.local,_subtype" syntax with the registration
1023 si.serviceType = si.serviceType + ",_subtype"
1024
1025 val registrationRecord = NsdRegistrationRecord()
1026
1027 val baseTypeDiscoveryRecord = NsdDiscoveryRecord()
1028 val subtypeDiscoveryRecord = NsdDiscoveryRecord()
1029 val otherSubtypeDiscoveryRecord = NsdDiscoveryRecord()
1030 tryTest {
1031 registerService(registrationRecord, si)
1032
1033 // Test "_subtype._type._tcp.local" syntax with discovery. Note this is not
1034 // "_subtype._sub._type._tcp.local".
1035 nsdManager.discoverServices(serviceType,
1036 NsdManager.PROTOCOL_DNS_SD,
1037 testNetwork1.network, Executor { it.run() }, baseTypeDiscoveryRecord)
1038 nsdManager.discoverServices("_othersubtype.$serviceType",
1039 NsdManager.PROTOCOL_DNS_SD,
1040 testNetwork1.network, Executor { it.run() }, otherSubtypeDiscoveryRecord)
1041 nsdManager.discoverServices("_subtype.$serviceType",
1042 NsdManager.PROTOCOL_DNS_SD,
1043 testNetwork1.network, Executor { it.run() }, subtypeDiscoveryRecord)
1044
1045 subtypeDiscoveryRecord.waitForServiceDiscovered(
1046 serviceName, serviceType, testNetwork1.network)
1047 baseTypeDiscoveryRecord.waitForServiceDiscovered(
1048 serviceName, serviceType, testNetwork1.network)
1049 otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStarted>()
1050 // The subtype callback was registered later but called, no need for an extra delay
1051 otherSubtypeDiscoveryRecord.assertNoCallback(timeoutMs = 0)
1052 } cleanupStep {
1053 nsdManager.stopServiceDiscovery(baseTypeDiscoveryRecord)
1054 nsdManager.stopServiceDiscovery(subtypeDiscoveryRecord)
1055 nsdManager.stopServiceDiscovery(otherSubtypeDiscoveryRecord)
1056
1057 baseTypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1058 subtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1059 otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1060 } cleanup {
1061 nsdManager.unregisterService(registrationRecord)
1062 }
1063 }
1064
1065 @Test
1066 fun testSubtypeAdvertisingAndDiscovery_withSetSubtypesApi() {
1067 runSubtypeAdvertisingAndDiscoveryTest(useLegacySpecifier = false)
1068 }
1069
1070 @Test
1071 fun testSubtypeAdvertisingAndDiscovery_withSetSubtypesApiAndLegacySpecifier() {
1072 runSubtypeAdvertisingAndDiscoveryTest(useLegacySpecifier = true)
1073 }
1074
1075 private fun runSubtypeAdvertisingAndDiscoveryTest(useLegacySpecifier: Boolean) {
1076 val si = makeTestServiceInfo(network = testNetwork1.network)
1077 if (useLegacySpecifier) {
1078 si.subtypes = setOf("_subtype1")
1079
1080 // Test "_type._tcp.local,_subtype" syntax with the registration
1081 si.serviceType = si.serviceType + ",_subtype2"
1082 } else {
1083 si.subtypes = setOf("_subtype1", "_subtype2")
1084 }
1085
1086 val registrationRecord = NsdRegistrationRecord()
1087
1088 val baseTypeDiscoveryRecord = NsdDiscoveryRecord()
1089 val subtype1DiscoveryRecord = NsdDiscoveryRecord()
1090 val subtype2DiscoveryRecord = NsdDiscoveryRecord()
1091 val otherSubtypeDiscoveryRecord = NsdDiscoveryRecord()
1092 tryTest {
1093 registerService(registrationRecord, si)
1094
1095 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
1096 testNetwork1.network, Executor { it.run() }, baseTypeDiscoveryRecord)
1097
1098 // Test "<subtype>._type._tcp.local" syntax with discovery. Note this is not
1099 // "<subtype>._sub._type._tcp.local".
1100 nsdManager.discoverServices("_othersubtype.$serviceType",
1101 NsdManager.PROTOCOL_DNS_SD,
1102 testNetwork1.network, Executor { it.run() }, otherSubtypeDiscoveryRecord)
1103 nsdManager.discoverServices("_subtype1.$serviceType",
1104 NsdManager.PROTOCOL_DNS_SD,
1105 testNetwork1.network, Executor { it.run() }, subtype1DiscoveryRecord)
1106
1107 nsdManager.discoverServices(
1108 DiscoveryRequest.Builder(serviceType).setSubtype("_subtype2")
1109 .setNetwork(testNetwork1.network).build(),
1110 Executor { it.run() }, subtype2DiscoveryRecord)
1111
1112 val info1 = subtype1DiscoveryRecord.waitForServiceDiscovered(
1113 serviceName, serviceType, testNetwork1.network)
1114 assertTrue(info1.subtypes.contains("_subtype1"))
1115 val info2 = subtype2DiscoveryRecord.waitForServiceDiscovered(
1116 serviceName, serviceType, testNetwork1.network)
1117 assertTrue(info2.subtypes.contains("_subtype2"))
1118 baseTypeDiscoveryRecord.waitForServiceDiscovered(
1119 serviceName, serviceType, testNetwork1.network)
1120 otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStarted>()
1121 // The subtype callback was registered later but called, no need for an extra delay
1122 otherSubtypeDiscoveryRecord.assertNoCallback(timeoutMs = 0)
1123 } cleanupStep {
1124 nsdManager.stopServiceDiscovery(baseTypeDiscoveryRecord)
1125 nsdManager.stopServiceDiscovery(subtype1DiscoveryRecord)
1126 nsdManager.stopServiceDiscovery(subtype2DiscoveryRecord)
1127 nsdManager.stopServiceDiscovery(otherSubtypeDiscoveryRecord)
1128
1129 baseTypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1130 subtype1DiscoveryRecord.expectCallback<DiscoveryStopped>()
1131 subtype2DiscoveryRecord.expectCallback<DiscoveryStopped>()
1132 otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
1133 } cleanup {
1134 nsdManager.unregisterService(registrationRecord)
1135 }
1136 }
1137
1138 @Test
1139 fun testMultipleSubTypeAdvertisingAndDiscovery_withUpdate() {
1140 val si1 = makeTestServiceInfo(network = testNetwork1.network).apply {
1141 serviceType += ",_subtype1"
1142 }
1143 val si2 = makeTestServiceInfo(network = testNetwork1.network).apply {
1144 serviceType += ",_subtype2"
1145 }
1146 val registrationRecord = NsdRegistrationRecord()
1147 val subtype3DiscoveryRecord = NsdDiscoveryRecord()
1148 tryTest {
1149 registerService(registrationRecord, si1)
1150 updateService(registrationRecord, si2)
1151 nsdManager.discoverServices("_subtype2.$serviceType",
1152 NsdManager.PROTOCOL_DNS_SD, testNetwork1.network,
1153 { it.run() }, subtype3DiscoveryRecord)
1154 subtype3DiscoveryRecord.waitForServiceDiscovered(serviceName,
1155 serviceType, testNetwork1.network)
1156 } cleanupStep {
1157 nsdManager.stopServiceDiscovery(subtype3DiscoveryRecord)
1158 subtype3DiscoveryRecord.expectCallback<DiscoveryStopped>()
1159 } cleanup {
1160 nsdManager.unregisterService(registrationRecord)
1161 }
1162 }
1163
1164 @Test
1165 fun testSubtypeAdvertisingAndDiscovery_nonAlphanumericalSubtypes() {
1166 // All non-alphanumerical characters between 0x20 and 0x7e, with a leading underscore
1167 val nonAlphanumSubtype = "_ !\"#\$%&'()*+-/:;<=>?@[\\]^_`{|}"
1168 // Test both legacy syntax and the subtypes setter, on different networks
1169 val si1 = makeTestServiceInfo(network = testNetwork1.network).apply {
1170 serviceType = "$serviceType,_test1,$nonAlphanumSubtype"
1171 }
1172 val si2 = makeTestServiceInfo(network = testNetwork2.network).apply {
1173 subtypes = setOf("_test2", nonAlphanumSubtype)
1174 }
1175
1176 val registrationRecord1 = NsdRegistrationRecord()
1177 val registrationRecord2 = NsdRegistrationRecord()
1178 val subtypeDiscoveryRecord1 = NsdDiscoveryRecord()
1179 val subtypeDiscoveryRecord2 = NsdDiscoveryRecord()
1180 tryTest {
1181 registerService(registrationRecord1, si1)
1182 registerService(registrationRecord2, si2)
1183 nsdManager.discoverServices(DiscoveryRequest.Builder(serviceType)
1184 .setSubtype(nonAlphanumSubtype)
1185 .setNetwork(testNetwork1.network)
1186 .build(), { it.run() }, subtypeDiscoveryRecord1)
1187 nsdManager.discoverServices("$nonAlphanumSubtype.$serviceType",
1188 NsdManager.PROTOCOL_DNS_SD, testNetwork2.network, { it.run() },
1189 subtypeDiscoveryRecord2)
1190
1191 val discoveredInfo1 = subtypeDiscoveryRecord1.waitForServiceDiscovered(serviceName,
1192 serviceType, testNetwork1.network)
1193 val discoveredInfo2 = subtypeDiscoveryRecord2.waitForServiceDiscovered(serviceName,
1194 serviceType, testNetwork2.network)
1195 assertTrue(discoveredInfo1.subtypes.contains(nonAlphanumSubtype))
1196 assertTrue(discoveredInfo2.subtypes.contains(nonAlphanumSubtype))
1197 } cleanupStep {
1198 nsdManager.stopServiceDiscovery(subtypeDiscoveryRecord1)
1199 subtypeDiscoveryRecord1.expectCallback<DiscoveryStopped>()
1200 } cleanupStep {
1201 nsdManager.stopServiceDiscovery(subtypeDiscoveryRecord2)
1202 subtypeDiscoveryRecord2.expectCallback<DiscoveryStopped>()
1203 } cleanup {
1204 nsdManager.unregisterService(registrationRecord1)
1205 nsdManager.unregisterService(registrationRecord2)
1206 }
1207 }
1208
1209 @Test
1210 fun testSubtypeDiscovery_typeMatchButSubtypeNotMatch_notDiscovered() {
1211 val si1 = makeTestServiceInfo(network = testNetwork1.network).apply {
1212 serviceType += ",_subtype1"
1213 }
1214 val registrationRecord = NsdRegistrationRecord()
1215 val subtype2DiscoveryRecord = NsdDiscoveryRecord()
1216 tryTest {
1217 registerService(registrationRecord, si1)
1218 val request = DiscoveryRequest.Builder(serviceType)
1219 .setSubtype("_subtype2").setNetwork(testNetwork1.network).build()
1220 nsdManager.discoverServices(request, { it.run() }, subtype2DiscoveryRecord)
1221 subtype2DiscoveryRecord.expectCallback<DiscoveryStarted>()
1222 subtype2DiscoveryRecord.assertNoCallback(timeoutMs = 2000)
1223 } cleanupStep {
1224 nsdManager.stopServiceDiscovery(subtype2DiscoveryRecord)
1225 subtype2DiscoveryRecord.expectCallback<DiscoveryStopped>()
1226 } cleanup {
1227 nsdManager.unregisterService(registrationRecord)
1228 }
1229 }
1230
1231 @Test
1232 fun testSubtypeAdvertising_tooManySubtypes_returnsFailureBadParameters() {
1233 val si = makeTestServiceInfo(network = testNetwork1.network)
1234 // Sets 101 subtypes in total
1235 val seq = generateSequence(1) { it + 1}
1236 si.subtypes = seq.take(100).toList().map {it -> "_subtype" + it}.toSet()
1237 si.serviceType = si.serviceType + ",_subtype"
1238
1239 val record = NsdRegistrationRecord()
1240 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, Executor { it.run() }, record)
1241
1242 val failedCb = record.expectCallback<RegistrationFailed>(REGISTRATION_TIMEOUT_MS)
1243 assertEquals(NsdManager.FAILURE_BAD_PARAMETERS, failedCb.errorCode)
1244 }
1245
1246 @Test
1247 fun testSubtypeAdvertising_emptySubtypeLabel_returnsFailureBadParameters() {
1248 val si = makeTestServiceInfo(network = testNetwork1.network)
1249 si.subtypes = setOf("")
1250
1251 val record = NsdRegistrationRecord()
1252 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, Executor { it.run() }, record)
1253
1254 val failedCb = record.expectCallback<RegistrationFailed>(REGISTRATION_TIMEOUT_MS)
1255 assertEquals(NsdManager.FAILURE_BAD_PARAMETERS, failedCb.errorCode)
1256 }
1257
1258 @Test
1259 fun testRegisterWithConflictDuringProbing() {
1260 // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
1261 assumeTrue(TestUtils.shouldTestTApis())
1262
1263 val si = makeTestServiceInfo(testNetwork1.network)
1264
1265 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1266 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1267 packetReader.startAsyncForTest()
1268 handlerThread.waitForIdle(TIMEOUT_MS)
1269
1270 // Register service on testNetwork1
1271 val registrationRecord = NsdRegistrationRecord()
1272 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, { it.run() },
1273 registrationRecord)
1274
1275 tryTest {
1276 assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
1277 "Did not find a probe for the service")
1278 packetReader.sendResponse(buildConflictingAnnouncement())
1279
1280 // Registration must use an updated name to avoid the conflict
1281 val cb = registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
1282 cb.serviceInfo.serviceName.let {
1283 assertTrue("Unexpected registered name: $it",
1284 it.startsWith(serviceName) && it != serviceName)
1285 }
1286 } cleanupStep {
1287 nsdManager.unregisterService(registrationRecord)
1288 registrationRecord.expectCallback<ServiceUnregistered>()
1289 } cleanup {
1290 packetReader.handler.post { packetReader.stop() }
1291 handlerThread.waitForIdle(TIMEOUT_MS)
1292 }
1293 }
1294
1295 @Test
1296 fun testRegisterServiceWithCustomHostAndAddresses_conflictDuringProbing_hostRenamed() {
1297 val si = makeTestServiceInfo(testNetwork1.network).apply {
1298 hostname = customHostname
1299 hostAddresses = listOf(
1300 parseNumericAddress("192.0.2.24"),
1301 parseNumericAddress("2001:db8::3"))
1302 }
1303
1304 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1305 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1306 packetReader.startAsyncForTest()
1307 handlerThread.waitForIdle(TIMEOUT_MS)
1308
1309 // Register service on testNetwork1
1310 val registrationRecord = NsdRegistrationRecord()
1311 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, { it.run() },
1312 registrationRecord)
1313
1314 tryTest {
1315 assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
1316 "Did not find a probe for the service")
1317 packetReader.sendResponse(buildConflictingAnnouncementForCustomHost())
1318
1319 // Registration must use an updated hostname to avoid the conflict
1320 val cb = registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
1321 // Service name is not renamed because there's no conflict on the service name.
1322 assertEquals(serviceName, cb.serviceInfo.serviceName)
1323 val hostname = cb.serviceInfo.hostname ?: fail("Missing hostname")
1324 hostname.let {
1325 assertTrue("Unexpected registered hostname: $it",
1326 it.startsWith(customHostname) && it != customHostname)
1327 }
1328 } cleanupStep {
1329 nsdManager.unregisterService(registrationRecord)
1330 registrationRecord.expectCallback<ServiceUnregistered>()
1331 } cleanup {
1332 packetReader.handler.post { packetReader.stop() }
1333 handlerThread.waitForIdle(TIMEOUT_MS)
1334 }
1335 }
1336
1337 @Test
1338 fun testRegisterServiceWithCustomHostNoAddresses_noConflictDuringProbing_notRenamed() {
1339 val si = makeTestServiceInfo(testNetwork1.network).apply {
1340 hostname = customHostname
1341 }
1342
1343 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1344 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1345 packetReader.startAsyncForTest()
1346 handlerThread.waitForIdle(TIMEOUT_MS)
1347
1348 // Register service on testNetwork1
1349 val registrationRecord = NsdRegistrationRecord()
1350 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, { it.run() },
1351 registrationRecord)
1352
1353 tryTest {
1354 assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
1355 "Did not find a probe for the service")
1356 // Not a conflict because no record is registered for the hostname
1357 packetReader.sendResponse(buildConflictingAnnouncementForCustomHost())
1358
1359 // Registration is not renamed because there's no conflict
1360 val cb = registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
1361 assertEquals(serviceName, cb.serviceInfo.serviceName)
1362 assertEquals(customHostname, cb.serviceInfo.hostname)
1363 } cleanupStep {
1364 nsdManager.unregisterService(registrationRecord)
1365 registrationRecord.expectCallback<ServiceUnregistered>()
1366 } cleanup {
1367 packetReader.handler.post { packetReader.stop() }
1368 handlerThread.waitForIdle(TIMEOUT_MS)
1369 }
1370 }
1371
1372 @Test
1373 fun testRegisterWithConflictAfterProbing() {
1374 // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
1375 assumeTrue(TestUtils.shouldTestTApis())
1376
1377 val si = makeTestServiceInfo(testNetwork1.network)
1378
1379 // Register service on testNetwork1
1380 val registrationRecord = NsdRegistrationRecord()
1381 val discoveryRecord = NsdDiscoveryRecord()
1382 val registeredService = registerService(registrationRecord, si)
1383 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1384 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1385 packetReader.startAsyncForTest()
1386 handlerThread.waitForIdle(TIMEOUT_MS)
1387
1388 tryTest {
1389 assertNotNull(packetReader.pollForAdvertisement(serviceName, serviceType),
1390 "No announcements sent after initial probing")
1391
1392 assertEquals(si.serviceName, registeredService.serviceName)
1393
1394 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
1395 testNetwork1.network, { it.run() }, discoveryRecord)
1396 discoveryRecord.waitForServiceDiscovered(si.serviceName, serviceType)
1397
1398 // Send a conflicting announcement
1399 val conflictingAnnouncement = buildConflictingAnnouncement()
1400 packetReader.sendResponse(conflictingAnnouncement)
1401
1402 // Expect to see probes (RFC6762 9., service is reset to probing state)
1403 assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
1404 "Probe not received within timeout after conflict")
1405
1406 // Send the conflicting packet again to reply to the probe
1407 packetReader.sendResponse(conflictingAnnouncement)
1408
1409 // Note the legacy mdnsresponder would send an exit announcement here (a 0-lifetime
1410 // advertisement just for the PTR record), but not the new advertiser. This probably
1411 // follows RFC 6762 8.4, saying that when a record rdata changed, "In the case of shared
1412 // records, a host MUST send a "goodbye" announcement with RR TTL zero [...] for the old
1413 // rdata, to cause it to be deleted from peer caches, before announcing the new rdata".
1414 //
1415 // This should be implemented by the new advertiser, but in the case of conflicts it is
1416 // not very valuable since an identical PTR record would be used by the conflicting
1417 // service (except for subtypes). In that case the exit announcement may be
1418 // counter-productive as it conflicts with announcements done by the conflicting
1419 // service.
1420
1421 // Note that before sending the following ServiceRegistered callback for the renamed
1422 // service, the legacy mdnsresponder-based implementation would first send a
1423 // Service*Registered* callback for the original service name being *unregistered*; it
1424 // should have been a ServiceUnregistered callback instead (bug in NsdService
1425 // interpretation of the callback).
1426 val newRegistration = registrationRecord.expectCallbackEventually<ServiceRegistered>(
1427 REGISTRATION_TIMEOUT_MS) {
1428 it.serviceInfo.serviceName.startsWith(serviceName) &&
1429 it.serviceInfo.serviceName != serviceName
1430 }
1431
1432 discoveryRecord.expectCallbackEventually<ServiceFound> {
1433 it.serviceInfo.serviceName == newRegistration.serviceInfo.serviceName
1434 }
1435 } cleanupStep {
1436 nsdManager.stopServiceDiscovery(discoveryRecord)
1437 discoveryRecord.expectCallback<DiscoveryStopped>()
1438 } cleanupStep {
1439 nsdManager.unregisterService(registrationRecord)
1440 registrationRecord.expectCallback<ServiceUnregistered>()
1441 } cleanup {
1442 packetReader.handler.post { packetReader.stop() }
1443 handlerThread.waitForIdle(TIMEOUT_MS)
1444 }
1445 }
1446
1447 @Test
1448 fun testRegisterServiceWithCustomHostAndAddresses_conflictAfterProbing_hostRenamed() {
1449 val si = makeTestServiceInfo(testNetwork1.network).apply {
1450 hostname = customHostname
1451 hostAddresses = listOf(
1452 parseNumericAddress("192.0.2.24"),
1453 parseNumericAddress("2001:db8::3"))
1454 }
1455
1456 // Register service on testNetwork1
1457 val registrationRecord = NsdRegistrationRecord()
1458 val discoveryRecord = NsdDiscoveryRecord()
1459 val registeredService = registerService(registrationRecord, si)
1460 val packetReader = TapPacketReader(
1461 Handler(handlerThread.looper),
1462 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1463 packetReader.startAsyncForTest()
1464 handlerThread.waitForIdle(TIMEOUT_MS)
1465
1466 tryTest {
1467 assertNotNull(packetReader.pollForAdvertisement(serviceName, serviceType),
1468 "No announcements sent after initial probing")
1469
1470 assertEquals(si.serviceName, registeredService.serviceName)
1471 assertEquals(si.hostname, registeredService.hostname)
1472
1473 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
1474 testNetwork1.network, { it.run() }, discoveryRecord)
1475 val discoveredInfo = discoveryRecord.waitForServiceDiscovered(
1476 si.serviceName, serviceType)
1477
1478 // Send a conflicting announcement
1479 val conflictingAnnouncement = buildConflictingAnnouncementForCustomHost()
1480 packetReader.sendResponse(conflictingAnnouncement)
1481
1482 // Expect to see probes (RFC6762 9., service is reset to probing state)
1483 assertNotNull(packetReader.pollForProbe(serviceName, serviceType),
1484 "Probe not received within timeout after conflict")
1485
1486 // Send the conflicting packet again to reply to the probe
1487 packetReader.sendResponse(conflictingAnnouncement)
1488
1489 val newRegistration =
1490 registrationRecord
1491 .expectCallbackEventually<ServiceRegistered>(REGISTRATION_TIMEOUT_MS) {
1492 it.serviceInfo.serviceName == serviceName
1493 && it.serviceInfo.hostname.let { hostname ->
1494 hostname != null
1495 && hostname.startsWith(customHostname)
1496 && hostname != customHostname
1497 }
1498 }
1499
1500 val resolvedInfo = resolveService(discoveredInfo)
1501 assertEquals(newRegistration.serviceInfo.serviceName, resolvedInfo.serviceName)
1502 assertEquals(newRegistration.serviceInfo.hostname, resolvedInfo.hostname)
1503
1504 discoveryRecord.assertNoCallback()
1505 } cleanupStep {
1506 nsdManager.stopServiceDiscovery(discoveryRecord)
1507 discoveryRecord.expectCallback<DiscoveryStopped>()
1508 } cleanupStep {
1509 nsdManager.unregisterService(registrationRecord)
1510 registrationRecord.expectCallback<ServiceUnregistered>()
1511 } cleanup {
1512 packetReader.handler.post { packetReader.stop() }
1513 handlerThread.waitForIdle(TIMEOUT_MS)
1514 }
1515 }
1516
1517 @Test
1518 fun testRegisterServiceWithCustomHostNoAddresses_noConflictAfterProbing_notRenamed() {
1519 val si = makeTestServiceInfo(testNetwork1.network).apply {
1520 hostname = customHostname
1521 }
1522
1523 // Register service on testNetwork1
1524 val registrationRecord = NsdRegistrationRecord()
1525 val discoveryRecord = NsdDiscoveryRecord()
1526 val registeredService = registerService(registrationRecord, si)
1527 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1528 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1529 packetReader.startAsyncForTest()
1530 handlerThread.waitForIdle(TIMEOUT_MS)
1531
1532 tryTest {
1533 assertNotNull(packetReader.pollForAdvertisement(serviceName, serviceType),
1534 "No announcements sent after initial probing")
1535
1536 assertEquals(si.serviceName, registeredService.serviceName)
1537 assertEquals(si.hostname, registeredService.hostname)
1538
1539 // Send a conflicting announcement
1540 val conflictingAnnouncement = buildConflictingAnnouncementForCustomHost()
1541 packetReader.sendResponse(conflictingAnnouncement)
1542
1543 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
1544 testNetwork1.network, { it.run() }, discoveryRecord)
1545
1546 // The service is not renamed
1547 discoveryRecord.waitForServiceDiscovered(si.serviceName, serviceType)
1548 } cleanupStep {
1549 nsdManager.stopServiceDiscovery(discoveryRecord)
1550 discoveryRecord.expectCallback<DiscoveryStopped>()
1551 } cleanupStep {
1552 nsdManager.unregisterService(registrationRecord)
1553 registrationRecord.expectCallback<ServiceUnregistered>()
1554 } cleanup {
1555 packetReader.handler.post { packetReader.stop() }
1556 handlerThread.waitForIdle(TIMEOUT_MS)
1557 }
1558 }
1559
1560 // Test that even if only a PTR record is received as a reply when discovering, without the
1561 // SRV, TXT, address records as recommended (but not mandated) by RFC 6763 12, the service can
1562 // still be discovered.
1563 @Test
1564 fun testDiscoveryWithPtrOnlyResponse_ServiceIsFound() {
1565 // Register service on testNetwork1
1566 val discoveryRecord = NsdDiscoveryRecord()
1567 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1568 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1569 packetReader.startAsyncForTest()
1570 handlerThread.waitForIdle(TIMEOUT_MS)
1571
1572 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
1573 testNetwork1.network, { it.run() }, discoveryRecord)
1574
1575 tryTest {
1576 discoveryRecord.expectCallback<DiscoveryStarted>()
1577 assertNotNull(packetReader.pollForQuery("$serviceType.local", DnsResolver.TYPE_PTR))
1578 /*
1579 Generated with:
1580 scapy.raw(scapy.DNS(rd=0, qr=1, aa=1, qd = None, an =
1581 scapy.DNSRR(rrname='_nmt123456789._tcp.local', type='PTR', ttl=120,
1582 rdata='NsdTest123456789._nmt123456789._tcp.local'))).hex()
1583 */
1584 val ptrResponsePayload = HexDump.hexStringToByteArray("0000840000000001000000000d5f6e" +
1585 "6d74313233343536373839045f746370056c6f63616c00000c000100000078002b104e736454" +
1586 "6573743132333435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00")
1587
1588 replaceServiceNameAndTypeWithTestSuffix(ptrResponsePayload)
1589 packetReader.sendResponse(buildMdnsPacket(ptrResponsePayload))
1590
1591 val serviceFound = discoveryRecord.expectCallback<ServiceFound>()
1592 serviceFound.serviceInfo.let {
1593 assertEquals(serviceName, it.serviceName)
1594 // Discovered service types have a dot at the end
1595 assertEquals("$serviceType.", it.serviceType)
1596 assertEquals(testNetwork1.network, it.network)
1597 // ServiceFound does not provide port, address or attributes (only information
1598 // available in the PTR record is included in that callback, regardless of whether
1599 // other records exist).
1600 assertEquals(0, it.port)
1601 assertEmpty(it.hostAddresses)
1602 assertEquals(0, it.attributes.size)
1603 }
1604 } cleanup {
1605 nsdManager.stopServiceDiscovery(discoveryRecord)
1606 discoveryRecord.expectCallback<DiscoveryStopped>()
1607 }
1608 }
1609
1610 // Test RFC 6763 12. "Clients MUST be capable of functioning correctly with DNS servers [...]
1611 // that fail to generate these additional records automatically, by issuing subsequent queries
1612 // for any further record(s) they require"
1613 @Test
1614 fun testResolveWhenServerSendsNoAdditionalRecord() {
1615 // Resolve service on testNetwork1
1616 val resolveRecord = NsdResolveRecord()
1617 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1618 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */
1619 )
1620 packetReader.startAsyncForTest()
1621 handlerThread.waitForIdle(TIMEOUT_MS)
1622
1623 val si = makeTestServiceInfo(testNetwork1.network)
1624 nsdManager.resolveService(si, { it.run() }, resolveRecord)
1625
1626 val serviceFullName = "$serviceName.$serviceType.local"
1627 // The query should ask for ANY, since both SRV and TXT are requested. Note legacy
1628 // mdnsresponder will ask for SRV and TXT separately, and will not proceed to asking for
1629 // address records without an answer for both.
1630 val srvTxtQuery = packetReader.pollForQuery(serviceFullName, DnsResolver.TYPE_ANY)
1631 assertNotNull(srvTxtQuery)
1632
1633 /*
1634 Generated with:
1635 scapy.raw(scapy.dns_compress(scapy.DNS(rd=0, qr=1, aa=1, qd = None, an =
1636 scapy.DNSRRSRV(rrname='NsdTest123456789._nmt123456789._tcp.local',
1637 rclass=0x8001, port=31234, target='testhost.local', ttl=120) /
1638 scapy.DNSRR(rrname='NsdTest123456789._nmt123456789._tcp.local', type='TXT', ttl=120,
1639 rdata='testkey=testvalue')
1640 ))).hex()
1641 */
1642 val srvTxtResponsePayload = HexDump.hexStringToByteArray("000084000000000200000000104" +
1643 "e7364546573743132333435363738390d5f6e6d74313233343536373839045f746370056c6f6" +
1644 "3616c0000218001000000780011000000007a020874657374686f7374c030c00c00100001000" +
1645 "00078001211746573746b65793d7465737476616c7565")
1646 replaceServiceNameAndTypeWithTestSuffix(srvTxtResponsePayload)
1647 packetReader.sendResponse(buildMdnsPacket(srvTxtResponsePayload))
1648
1649 val testHostname = "testhost.local"
1650 val addressQuery = packetReader.pollForQuery(testHostname,
1651 DnsResolver.TYPE_A, DnsResolver.TYPE_AAAA)
1652 assertNotNull(addressQuery)
1653
1654 /*
1655 Generated with:
1656 scapy.raw(scapy.dns_compress(scapy.DNS(rd=0, qr=1, aa=1, qd = None, an =
1657 scapy.DNSRR(rrname='testhost.local', type='A', ttl=120,
1658 rdata='192.0.2.123') /
1659 scapy.DNSRR(rrname='testhost.local', type='AAAA', ttl=120,
1660 rdata='2001:db8::123')
1661 ))).hex()
1662 */
1663 val addressPayload = HexDump.hexStringToByteArray("0000840000000002000000000874657374" +
1664 "686f7374056c6f63616c0000010001000000780004c000027bc00c001c000100000078001020" +
1665 "010db8000000000000000000000123")
1666 packetReader.sendResponse(buildMdnsPacket(addressPayload))
1667
1668 val serviceResolved = resolveRecord.expectCallback<ServiceResolved>()
1669 serviceResolved.serviceInfo.let {
1670 assertEquals(serviceName, it.serviceName)
1671 assertEquals(".$serviceType", it.serviceType)
1672 assertEquals(testNetwork1.network, it.network)
1673 assertEquals(31234, it.port)
1674 assertEquals(1, it.attributes.size)
1675 assertArrayEquals("testvalue".encodeToByteArray(), it.attributes["testkey"])
1676 }
1677 assertEquals(
1678 setOf(parseNumericAddress("192.0.2.123"), parseNumericAddress("2001:db8::123")),
1679 serviceResolved.serviceInfo.hostAddresses.toSet())
1680 }
1681
1682 @Test
1683 fun testUnicastReplyUsedWhenQueryUnicastFlagSet() {
1684 // The flag may be removed in the future but unicast replies should be enabled by default
1685 // in that case. The rule will reset flags automatically on teardown.
1686 deviceConfigRule.setConfig(NAMESPACE_TETHERING, "test_nsd_unicast_reply_enabled", "1")
1687
1688 val si = makeTestServiceInfo(testNetwork1.network)
1689
1690 // Register service on testNetwork1
1691 val registrationRecord = NsdRegistrationRecord()
1692 var nsResponder: NSResponder? = null
1693 tryTest {
1694 registerService(registrationRecord, si)
1695 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1696 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1697 packetReader.startAsyncForTest()
1698
1699 handlerThread.waitForIdle(TIMEOUT_MS)
1700 /*
1701 Send a "query unicast" query.
1702 Generated with:
1703 scapy.raw(scapy.DNS(rd=0, qr=0, aa=0, qd =
1704 scapy.DNSQR(qname='_nmt123456789._tcp.local', qtype='PTR', qclass=0x8001)
1705 )).hex()
1706 */
1707 val mdnsPayload = HexDump.hexStringToByteArray("0000000000010000000000000d5f6e6d74313" +
1708 "233343536373839045f746370056c6f63616c00000c8001")
1709 replaceServiceNameAndTypeWithTestSuffix(mdnsPayload)
1710
1711 val testSrcAddr = makeLinkLocalAddressOfOtherDeviceOnPrefix(testNetwork1.network)
1712 nsResponder = NSResponder(packetReader, mapOf(
1713 testSrcAddr to MacAddress.fromString("01:02:03:04:05:06")
1714 )).apply { start() }
1715
1716 packetReader.sendResponse(buildMdnsPacket(mdnsPayload, testSrcAddr))
1717 // The reply is sent unicast to the source address. There may be announcements sent
1718 // multicast around this time, so filter by destination address.
1719 val reply = packetReader.pollForMdnsPacket { pkt ->
1720 pkt.isReplyFor("$serviceType.local", DnsResolver.TYPE_PTR) &&
1721 pkt.dstAddr == testSrcAddr
1722 }
1723 assertNotNull(reply)
1724 } cleanup {
1725 nsResponder?.stop()
1726 nsdManager.unregisterService(registrationRecord)
1727 registrationRecord.expectCallback<ServiceUnregistered>()
1728 }
1729 }
1730
1731 @Test
1732 fun testReplyWhenKnownAnswerSuppressionFlagSet() {
1733 // The flag may be removed in the future but known-answer suppression should be enabled by
1734 // default in that case. The rule will reset flags automatically on teardown.
1735 deviceConfigRule.setConfig(NAMESPACE_TETHERING, "test_nsd_known_answer_suppression", "1")
1736 deviceConfigRule.setConfig(NAMESPACE_TETHERING, "test_nsd_unicast_reply_enabled", "1")
1737
1738 val si = makeTestServiceInfo(testNetwork1.network)
1739
1740 // Register service on testNetwork1
1741 val registrationRecord = NsdRegistrationRecord()
1742 var nsResponder: NSResponder? = null
1743 tryTest {
1744 registerService(registrationRecord, si)
1745 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1746 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1747 packetReader.startAsyncForTest()
1748
1749 handlerThread.waitForIdle(TIMEOUT_MS)
1750 /*
1751 Send a query with a known answer. Expect to receive a response containing TXT record
1752 only.
1753 Generated with:
1754 scapy.raw(scapy.DNS(rd=0, qr=0, aa=0, qd =
1755 scapy.DNSQR(qname='_nmt123456789._tcp.local', qtype='PTR',
1756 qclass=0x8001) /
1757 scapy.DNSQR(qname='NsdTest123456789._nmt123456789._tcp.local', qtype='TXT',
1758 qclass=0x8001),
1759 an = scapy.DNSRR(rrname='_nmt123456789._tcp.local', type='PTR', ttl=4500,
1760 rdata='NsdTest123456789._nmt123456789._tcp.local')
1761 )).hex()
1762 */
1763 val query = HexDump.hexStringToByteArray("0000000000020001000000000d5f6e6d74313233343" +
1764 "536373839045f746370056c6f63616c00000c8001104e7364546573743132333435363738390" +
1765 "d5f6e6d74313233343536373839045f746370056c6f63616c00001080010d5f6e6d743132333" +
1766 "43536373839045f746370056c6f63616c00000c000100001194002b104e73645465737431323" +
1767 "33435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00")
1768 replaceServiceNameAndTypeWithTestSuffix(query)
1769
1770 val testSrcAddr = makeLinkLocalAddressOfOtherDeviceOnPrefix(testNetwork1.network)
1771 nsResponder = NSResponder(packetReader, mapOf(
1772 testSrcAddr to MacAddress.fromString("01:02:03:04:05:06")
1773 )).apply { start() }
1774
1775 packetReader.sendResponse(buildMdnsPacket(query, testSrcAddr))
1776 // The reply is sent unicast to the source address. There may be announcements sent
1777 // multicast around this time, so filter by destination address.
1778 val reply = packetReader.pollForMdnsPacket { pkt ->
1779 pkt.isReplyFor("$serviceName.$serviceType.local", DnsResolver.TYPE_TXT) &&
1780 !pkt.isReplyFor("$serviceType.local", DnsResolver.TYPE_PTR) &&
1781 pkt.dstAddr == testSrcAddr
1782 }
1783 assertNotNull(reply)
1784
1785 /*
1786 Send a query with a known answer (TTL is less than half). Expect to receive a response
1787 containing both PTR and TXT records.
1788 Generated with:
1789 scapy.raw(scapy.DNS(rd=0, qr=0, aa=0, qd =
1790 scapy.DNSQR(qname='_nmt123456789._tcp.local', qtype='PTR',
1791 qclass=0x8001) /
1792 scapy.DNSQR(qname='NsdTest123456789._nmt123456789._tcp.local', qtype='TXT',
1793 qclass=0x8001),
1794 an = scapy.DNSRR(rrname='_nmt123456789._tcp.local', type='PTR', ttl=2150,
1795 rdata='NsdTest123456789._nmt123456789._tcp.local')
1796 )).hex()
1797 */
1798 val query2 = HexDump.hexStringToByteArray("0000000000020001000000000d5f6e6d7431323334" +
1799 "3536373839045f746370056c6f63616c00000c8001104e736454657374313233343536373839" +
1800 "0d5f6e6d74313233343536373839045f746370056c6f63616c00001080010d5f6e6d74313233" +
1801 "343536373839045f746370056c6f63616c00000c000100000866002b104e7364546573743132" +
1802 "333435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00")
1803 replaceServiceNameAndTypeWithTestSuffix(query2)
1804
1805 packetReader.sendResponse(buildMdnsPacket(query2, testSrcAddr))
1806 // The reply is sent unicast to the source address. There may be announcements sent
1807 // multicast around this time, so filter by destination address.
1808 val reply2 = packetReader.pollForMdnsPacket { pkt ->
1809 pkt.isReplyFor("$serviceName.$serviceType.local", DnsResolver.TYPE_TXT) &&
1810 pkt.isReplyFor("$serviceType.local", DnsResolver.TYPE_PTR) &&
1811 pkt.dstAddr == testSrcAddr
1812 }
1813 assertNotNull(reply2)
1814 } cleanup {
1815 nsResponder?.stop()
1816 nsdManager.unregisterService(registrationRecord)
1817 registrationRecord.expectCallback<ServiceUnregistered>()
1818 }
1819 }
1820
1821 @Test
1822 fun testReplyWithMultipacketWhenKnownAnswerSuppressionFlagSet() {
1823 // The flag may be removed in the future but known-answer suppression should be enabled by
1824 // default in that case. The rule will reset flags automatically on teardown.
1825 deviceConfigRule.setConfig(NAMESPACE_TETHERING, "test_nsd_known_answer_suppression", "1")
1826 deviceConfigRule.setConfig(NAMESPACE_TETHERING, "test_nsd_unicast_reply_enabled", "1")
1827
1828 val si = makeTestServiceInfo(testNetwork1.network)
1829
1830 // Register service on testNetwork1
1831 val registrationRecord = NsdRegistrationRecord()
1832 var nsResponder: NSResponder? = null
1833 tryTest {
1834 registerService(registrationRecord, si)
1835 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1836 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1837 packetReader.startAsyncForTest()
1838
1839 handlerThread.waitForIdle(TIMEOUT_MS)
1840 /*
1841 Send a query with truncated bit set.
1842 Generated with:
1843 scapy.raw(scapy.DNS(rd=0, qr=0, aa=0, tc=1, qd=
1844 scapy.DNSQR(qname='_nmt123456789._tcp.local', qtype='PTR',
1845 qclass=0x8001) /
1846 scapy.DNSQR(qname='NsdTest123456789._nmt123456789._tcp.local', qtype='TXT',
1847 qclass=0x8001)
1848 )).hex()
1849 */
1850 val query = HexDump.hexStringToByteArray("0000020000020000000000000d5f6e6d74313233343" +
1851 "536373839045f746370056c6f63616c00000c8001104e7364546573743132333435363738390" +
1852 "d5f6e6d74313233343536373839045f746370056c6f63616c0000108001")
1853 replaceServiceNameAndTypeWithTestSuffix(query)
1854 /*
1855 Send a known answer packet (other service) with truncated bit set.
1856 Generated with:
1857 scapy.raw(scapy.DNS(rd=0, qr=0, aa=0, tc=1, qd=None,
1858 an = scapy.DNSRR(rrname='_test._tcp.local', type='PTR', ttl=4500,
1859 rdata='NsdTest._test._tcp.local')
1860 )).hex()
1861 */
1862 val knownAnswer1 = HexDump.hexStringToByteArray("000002000000000100000000055f74657374" +
1863 "045f746370056c6f63616c00000c000100001194001a074e736454657374055f74657374045f" +
1864 "746370056c6f63616c00")
1865 replaceServiceNameAndTypeWithTestSuffix(knownAnswer1)
1866 /*
1867 Send a known answer packet.
1868 Generated with:
1869 scapy.raw(scapy.DNS(rd=0, qr=0, aa=0, qd=None,
1870 an = scapy.DNSRR(rrname='_nmt123456789._tcp.local', type='PTR', ttl=4500,
1871 rdata='NsdTest123456789._nmt123456789._tcp.local')
1872 )).hex()
1873 */
1874 val knownAnswer2 = HexDump.hexStringToByteArray("0000000000000001000000000d5f6e6d7431" +
1875 "3233343536373839045f746370056c6f63616c00000c000100001194002b104e736454657374" +
1876 "3132333435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00")
1877 replaceServiceNameAndTypeWithTestSuffix(knownAnswer2)
1878
1879 val testSrcAddr = makeLinkLocalAddressOfOtherDeviceOnPrefix(testNetwork1.network)
1880 nsResponder = NSResponder(packetReader, mapOf(
1881 testSrcAddr to MacAddress.fromString("01:02:03:04:05:06")
1882 )).apply { start() }
1883
1884 packetReader.sendResponse(buildMdnsPacket(query, testSrcAddr))
1885 packetReader.sendResponse(buildMdnsPacket(knownAnswer1, testSrcAddr))
1886 packetReader.sendResponse(buildMdnsPacket(knownAnswer2, testSrcAddr))
1887 // The reply is sent unicast to the source address. There may be announcements sent
1888 // multicast around this time, so filter by destination address.
1889 val reply = packetReader.pollForMdnsPacket { pkt ->
1890 pkt.isReplyFor("$serviceName.$serviceType.local", DnsResolver.TYPE_TXT) &&
1891 !pkt.isReplyFor("$serviceType.local", DnsResolver.TYPE_PTR) &&
1892 pkt.dstAddr == testSrcAddr
1893 }
1894 assertNotNull(reply)
1895 } cleanup {
1896 nsResponder?.stop()
1897 nsdManager.unregisterService(registrationRecord)
1898 registrationRecord.expectCallback<ServiceUnregistered>()
1899 }
1900 }
1901
1902 @Test
1903 fun testQueryWhenKnownAnswerSuppressionFlagSet() {
1904 // The flag may be removed in the future but known-answer suppression should be enabled by
1905 // default in that case. The rule will reset flags automatically on teardown.
1906 deviceConfigRule.setConfig(NAMESPACE_TETHERING, "test_nsd_query_with_known_answer", "1")
1907
1908 // Register service on testNetwork1
1909 val discoveryRecord = NsdDiscoveryRecord()
1910 val packetReader = TapPacketReader(Handler(handlerThread.looper),
1911 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
1912 packetReader.startAsyncForTest()
1913 handlerThread.waitForIdle(TIMEOUT_MS)
1914
1915 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
1916 testNetwork1.network, { it.run() }, discoveryRecord)
1917
1918 tryTest {
1919 discoveryRecord.expectCallback<DiscoveryStarted>()
1920 assertNotNull(packetReader.pollForQuery("$serviceType.local", DnsResolver.TYPE_PTR))
1921 /*
1922 Generated with:
1923 scapy.raw(scapy.DNS(rd=0, qr=1, aa=1, qd = None, an =
1924 scapy.DNSRR(rrname='_nmt123456789._tcp.local', type='PTR', ttl=120,
1925 rdata='NsdTest123456789._nmt123456789._tcp.local'))).hex()
1926 */
1927 val ptrResponsePayload = HexDump.hexStringToByteArray("0000840000000001000000000d5f6e" +
1928 "6d74313233343536373839045f746370056c6f63616c00000c000100000078002b104e736454" +
1929 "6573743132333435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00")
1930
1931 replaceServiceNameAndTypeWithTestSuffix(ptrResponsePayload)
1932 packetReader.sendResponse(buildMdnsPacket(ptrResponsePayload))
1933
1934 val serviceFound = discoveryRecord.expectCallback<ServiceFound>()
1935 serviceFound.serviceInfo.let {
1936 assertEquals(serviceName, it.serviceName)
1937 // Discovered service types have a dot at the end
1938 assertEquals("$serviceType.", it.serviceType)
1939 assertEquals(testNetwork1.network, it.network)
1940 // ServiceFound does not provide port, address or attributes (only information
1941 // available in the PTR record is included in that callback, regardless of whether
1942 // other records exist).
1943 assertEquals(0, it.port)
1944 assertEmpty(it.hostAddresses)
1945 assertEquals(0, it.attributes.size)
1946 }
1947
1948 // Expect the second query with a known answer
1949 val query = packetReader.pollForMdnsPacket { pkt ->
1950 pkt.isQueryFor("$serviceType.local", DnsResolver.TYPE_PTR) &&
1951 pkt.isReplyFor("$serviceType.local", DnsResolver.TYPE_PTR)
1952 }
1953 assertNotNull(query)
1954 } cleanup {
1955 nsdManager.stopServiceDiscovery(discoveryRecord)
1956 discoveryRecord.expectCallback<DiscoveryStopped>()
1957 }
1958 }
1959
1960 private fun makeLinkLocalAddressOfOtherDeviceOnPrefix(network: Network): Inet6Address {
1961 val lp = cm.getLinkProperties(network) ?: fail("No LinkProperties for net $network")
1962 // Expect to have a /64 link-local address
1963 val linkAddr = lp.linkAddresses.firstOrNull {
1964 it.isIPv6 && it.scope == RT_SCOPE_LINK && it.prefixLength == 64
1965 } ?: fail("No /64 link-local address found in ${lp.linkAddresses} for net $network")
1966
1967 // Add one to the device address to simulate the address of another device on the prefix
1968 val addrBytes = linkAddr.address.address
1969 addrBytes[IPV6_ADDR_LEN - 1]++
1970 return Inet6Address.getByAddress(addrBytes) as Inet6Address
1971 }
1972
1973 @Test
1974 fun testAdvertisingAndDiscovery_servicesWithCustomHost_customHostAddressesFound() {
1975 val hostAddresses1 = listOf(
1976 parseNumericAddress("192.0.2.23"),
1977 parseNumericAddress("2001:db8::1"),
1978 parseNumericAddress("2001:db8::2"))
1979 val hostAddresses2 = listOf(
1980 parseNumericAddress("192.0.2.24"),
1981 parseNumericAddress("2001:db8::3"))
1982 val si1 = NsdServiceInfo().also {
1983 it.network = testNetwork1.network
1984 it.serviceName = serviceName
1985 it.serviceType = serviceType
1986 it.port = TEST_PORT
1987 it.hostname = customHostname
1988 it.hostAddresses = hostAddresses1
1989 }
1990 val si2 = NsdServiceInfo().also {
1991 it.network = testNetwork1.network
1992 it.serviceName = serviceName2
1993 it.serviceType = serviceType
1994 it.port = TEST_PORT + 1
1995 it.hostname = customHostname2
1996 it.hostAddresses = hostAddresses2
1997 }
1998 val registrationRecord1 = NsdRegistrationRecord()
1999 val registrationRecord2 = NsdRegistrationRecord()
2000
2001 val discoveryRecord1 = NsdDiscoveryRecord()
2002 val discoveryRecord2 = NsdDiscoveryRecord()
2003 tryTest {
2004 registerService(registrationRecord1, si1)
2005
2006 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
2007 testNetwork1.network, Executor { it.run() }, discoveryRecord1)
2008
2009 val discoveredInfo = discoveryRecord1.waitForServiceDiscovered(
2010 serviceName, serviceType, testNetwork1.network)
2011 val resolvedInfo = resolveService(discoveredInfo)
2012
2013 assertEquals(TEST_PORT, resolvedInfo.port)
2014 assertEquals(si1.hostname, resolvedInfo.hostname)
2015 assertAddressEquals(hostAddresses1, resolvedInfo.hostAddresses)
2016
2017 registerService(registrationRecord2, si2)
2018 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
2019 testNetwork1.network, Executor { it.run() }, discoveryRecord2)
2020
2021 val discoveredInfo2 = discoveryRecord2.waitForServiceDiscovered(
2022 serviceName2, serviceType, testNetwork1.network)
2023 val resolvedInfo2 = resolveService(discoveredInfo2)
2024
2025 assertEquals(TEST_PORT + 1, resolvedInfo2.port)
2026 assertEquals(si2.hostname, resolvedInfo2.hostname)
2027 assertAddressEquals(hostAddresses2, resolvedInfo2.hostAddresses)
2028 } cleanupStep {
2029 nsdManager.stopServiceDiscovery(discoveryRecord1)
2030 nsdManager.stopServiceDiscovery(discoveryRecord2)
2031
2032 discoveryRecord1.expectCallbackEventually<DiscoveryStopped>()
2033 discoveryRecord2.expectCallbackEventually<DiscoveryStopped>()
2034 } cleanup {
2035 nsdManager.unregisterService(registrationRecord1)
2036 nsdManager.unregisterService(registrationRecord2)
2037 }
2038 }
2039
2040 @Test
2041 fun testAdvertisingAndDiscovery_multipleRegistrationsForSameCustomHost_hostRenamed() {
2042 val hostAddresses1 = listOf(
2043 parseNumericAddress("192.0.2.23"),
2044 parseNumericAddress("2001:db8::1"),
2045 parseNumericAddress("2001:db8::2"))
2046 val hostAddresses2 = listOf(
2047 parseNumericAddress("192.0.2.24"),
2048 parseNumericAddress("2001:db8::3"))
2049 val si1 = NsdServiceInfo().also {
2050 it.network = testNetwork1.network
2051 it.hostname = customHostname
2052 it.hostAddresses = hostAddresses1
2053 }
2054 val si2 = NsdServiceInfo().also {
2055 it.network = testNetwork1.network
2056 it.serviceName = serviceName
2057 it.serviceType = serviceType
2058 it.port = TEST_PORT
2059 it.hostname = customHostname
2060 it.hostAddresses = hostAddresses2
2061 }
2062
2063 val registrationRecord1 = NsdRegistrationRecord()
2064 val registrationRecord2 = NsdRegistrationRecord()
2065
2066 val discoveryRecord = NsdDiscoveryRecord()
2067 tryTest {
2068 registerService(registrationRecord1, si1)
2069 registerService(registrationRecord2, si2)
2070
2071 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
2072 testNetwork1.network, Executor { it.run() }, discoveryRecord)
2073
2074 val discoveredInfo = discoveryRecord.waitForServiceDiscovered(
2075 serviceName, serviceType, testNetwork1.network)
2076 val resolvedInfo = resolveService(discoveredInfo)
2077
2078 assertEquals(TEST_PORT, resolvedInfo.port)
2079 assertNotEquals(si1.hostname, resolvedInfo.hostname)
2080 assertAddressEquals(hostAddresses2, resolvedInfo.hostAddresses)
2081 } cleanupStep {
2082 nsdManager.stopServiceDiscovery(discoveryRecord)
2083
2084 discoveryRecord.expectCallbackEventually<DiscoveryStopped>()
2085 } cleanup {
2086 nsdManager.unregisterService(registrationRecord1)
2087 nsdManager.unregisterService(registrationRecord2)
2088 }
2089 }
2090
2091 @Test
2092 fun testAdvertisingAndDiscovery_servicesWithTheSameCustomHostAddressOmitted_addressesFound() {
2093 val hostAddresses = listOf(
2094 parseNumericAddress("192.0.2.23"),
2095 parseNumericAddress("2001:db8::1"),
2096 parseNumericAddress("2001:db8::2"))
2097 val si1 = NsdServiceInfo().also {
2098 it.network = testNetwork1.network
2099 it.serviceType = serviceType
2100 it.serviceName = serviceName
2101 it.port = TEST_PORT
2102 it.hostname = customHostname
2103 it.hostAddresses = hostAddresses
2104 }
2105 val si2 = NsdServiceInfo().also {
2106 it.network = testNetwork1.network
2107 it.serviceType = serviceType
2108 it.serviceName = serviceName2
2109 it.port = TEST_PORT + 1
2110 it.hostname = customHostname
2111 }
2112
2113 val registrationRecord1 = NsdRegistrationRecord()
2114 val registrationRecord2 = NsdRegistrationRecord()
2115
2116 val discoveryRecord = NsdDiscoveryRecord()
2117 tryTest {
2118 registerService(registrationRecord1, si1)
2119
2120 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
2121 testNetwork1.network, Executor { it.run() }, discoveryRecord)
2122
2123 val discoveredInfo1 = discoveryRecord.waitForServiceDiscovered(
2124 serviceName, serviceType, testNetwork1.network)
2125 val resolvedInfo1 = resolveService(discoveredInfo1)
2126
2127 assertEquals(serviceName, discoveredInfo1.serviceName)
2128 assertEquals(TEST_PORT, resolvedInfo1.port)
2129 assertEquals(si1.hostname, resolvedInfo1.hostname)
2130 assertAddressEquals(hostAddresses, resolvedInfo1.hostAddresses)
2131
2132 registerService(registrationRecord2, si2)
2133
2134 val discoveredInfo2 = discoveryRecord.waitForServiceDiscovered(
2135 serviceName2, serviceType, testNetwork1.network)
2136 val resolvedInfo2 = resolveService(discoveredInfo2)
2137
2138 assertEquals(serviceName2, discoveredInfo2.serviceName)
2139 assertEquals(TEST_PORT + 1, resolvedInfo2.port)
2140 assertEquals(si2.hostname, resolvedInfo2.hostname)
2141 assertAddressEquals(hostAddresses, resolvedInfo2.hostAddresses)
2142 } cleanupStep {
2143 nsdManager.stopServiceDiscovery(discoveryRecord)
2144
2145 discoveryRecord.expectCallback<DiscoveryStopped>()
2146 } cleanup {
2147 nsdManager.unregisterService(registrationRecord1)
2148 nsdManager.unregisterService(registrationRecord2)
2149 }
2150 }
2151
2152 @Test
2153 fun testRegisterService_registerImmediatelyAfterUnregister_serviceFound() {
2154 val info1 = makeTestServiceInfo(network = testNetwork1.network).apply {
2155 serviceName = "service11111"
2156 port = 11111
2157 }
2158 val info2 = makeTestServiceInfo(network = testNetwork1.network).apply {
2159 serviceName = "service22222"
2160 port = 22222
2161 }
2162 val registrationRecord1 = NsdRegistrationRecord()
2163 val discoveryRecord1 = NsdDiscoveryRecord()
2164 val registrationRecord2 = NsdRegistrationRecord()
2165 val discoveryRecord2 = NsdDiscoveryRecord()
2166 tryTest {
2167 registerService(registrationRecord1, info1)
2168 nsdManager.discoverServices(serviceType,
2169 NsdManager.PROTOCOL_DNS_SD, testNetwork1.network, { it.run() },
2170 discoveryRecord1)
2171 discoveryRecord1.waitForServiceDiscovered(info1.serviceName,
2172 serviceType, testNetwork1.network)
2173 nsdManager.stopServiceDiscovery(discoveryRecord1)
2174
2175 nsdManager.unregisterService(registrationRecord1)
2176 registerService(registrationRecord2, info2)
2177 nsdManager.discoverServices(serviceType,
2178 NsdManager.PROTOCOL_DNS_SD, testNetwork1.network, { it.run() },
2179 discoveryRecord2)
2180 val infoDiscovered = discoveryRecord2.waitForServiceDiscovered(info2.serviceName,
2181 serviceType, testNetwork1.network)
2182 val infoResolved = resolveService(infoDiscovered)
2183 assertEquals(22222, infoResolved.port)
2184 } cleanupStep {
2185 nsdManager.stopServiceDiscovery(discoveryRecord2)
2186 discoveryRecord2.expectCallback<DiscoveryStopped>()
2187 } cleanup {
2188 nsdManager.unregisterService(registrationRecord2)
2189 }
2190 }
2191
2192 @Test
2193 fun testAdvertisingAndDiscovery_reregisterCustomHostWithDifferentAddresses_newAddressesFound() {
2194 val si1 = NsdServiceInfo().also {
2195 it.network = testNetwork1.network
2196 it.hostname = customHostname
2197 it.hostAddresses = listOf(
2198 parseNumericAddress("192.0.2.23"),
2199 parseNumericAddress("2001:db8::1"))
2200 }
2201 val si2 = NsdServiceInfo().also {
2202 it.network = testNetwork1.network
2203 it.serviceName = serviceName
2204 it.serviceType = serviceType
2205 it.hostname = customHostname
2206 it.port = TEST_PORT
2207 }
2208 val si3 = NsdServiceInfo().also {
2209 it.network = testNetwork1.network
2210 it.hostname = customHostname
2211 it.hostAddresses = listOf(
2212 parseNumericAddress("192.0.2.24"),
2213 parseNumericAddress("2001:db8::2"))
2214 }
2215
2216 val registrationRecord1 = NsdRegistrationRecord()
2217 val registrationRecord2 = NsdRegistrationRecord()
2218 val registrationRecord3 = NsdRegistrationRecord()
2219
2220 val discoveryRecord = NsdDiscoveryRecord()
2221
2222 tryTest {
2223 registerService(registrationRecord1, si1)
2224 registerService(registrationRecord2, si2)
2225
2226 nsdManager.unregisterService(registrationRecord1)
2227 registrationRecord1.expectCallback<ServiceUnregistered>()
2228
2229 registerService(registrationRecord3, si3)
2230
2231 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
2232 testNetwork1.network, Executor { it.run() }, discoveryRecord)
2233 val discoveredInfo = discoveryRecord.waitForServiceDiscovered(
2234 serviceName, serviceType, testNetwork1.network)
2235 val resolvedInfo = resolveService(discoveredInfo)
2236
2237 assertEquals(serviceName, discoveredInfo.serviceName)
2238 assertEquals(TEST_PORT, resolvedInfo.port)
2239 assertEquals(customHostname, resolvedInfo.hostname)
2240 assertAddressEquals(
2241 listOf(parseNumericAddress("192.0.2.24"), parseNumericAddress("2001:db8::2")),
2242 resolvedInfo.hostAddresses)
2243 } cleanupStep {
2244 nsdManager.stopServiceDiscovery(discoveryRecord)
2245 discoveryRecord.expectCallbackEventually<DiscoveryStopped>()
2246 } cleanup {
2247 nsdManager.unregisterService(registrationRecord2)
2248 nsdManager.unregisterService(registrationRecord3)
2249 }
2250 }
2251
2252 @Test
2253 fun testAdvertising_registerServiceAndPublicKey_keyAnnounced() {
2254 val si = NsdServiceInfo().also {
2255 it.network = testNetwork1.network
2256 it.serviceType = serviceType
2257 it.serviceName = serviceName
2258 it.port = TEST_PORT
2259 it.publicKey = publicKey
2260 }
2261 val packetReader = TapPacketReader(Handler(handlerThread.looper),
2262 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
2263 packetReader.startAsyncForTest()
2264 handlerThread.waitForIdle(TIMEOUT_MS)
2265
2266 val registrationRecord = NsdRegistrationRecord()
2267 val discoveryRecord = NsdDiscoveryRecord()
2268 tryTest {
2269 registerService(registrationRecord, si)
2270
2271 val announcement = packetReader.pollForReply(
2272 "$serviceName.$serviceType.local",
2273 TYPE_KEY
2274 )
2275 assertNotNull(announcement)
2276 val keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
2277 assertEquals(1, keyRecords.size)
2278 val actualRecord = keyRecords.get(0)
2279 assertEquals(TYPE_KEY, actualRecord.nsType)
2280 assertEquals("$serviceName.$serviceType.local", actualRecord.dName)
2281 assertEquals(NAME_RECORDS_TTL_MILLIS, actualRecord.ttl)
2282 assertArrayEquals(publicKey, actualRecord.rr)
2283
2284 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
2285 testNetwork1.network, Executor { it.run() }, discoveryRecord)
2286
2287 val discoveredInfo1 = discoveryRecord.waitForServiceDiscovered(
2288 serviceName, serviceType, testNetwork1.network)
2289 val resolvedInfo1 = resolveService(discoveredInfo1)
2290
2291 assertEquals(serviceName, discoveredInfo1.serviceName)
2292 assertEquals(TEST_PORT, resolvedInfo1.port)
2293 } cleanupStep {
2294 nsdManager.stopServiceDiscovery(discoveryRecord)
2295
2296 discoveryRecord.expectCallback<DiscoveryStopped>()
2297 } cleanup {
2298 nsdManager.unregisterService(registrationRecord)
2299 }
2300 }
2301
2302 @Test
2303 fun testAdvertising_registerCustomHostAndPublicKey_keyAnnounced() {
2304 val si = NsdServiceInfo().also {
2305 it.network = testNetwork1.network
2306 it.hostname = customHostname
2307 it.hostAddresses = listOf(
2308 parseNumericAddress("192.0.2.23"),
2309 parseNumericAddress("2001:db8::1"),
2310 parseNumericAddress("2001:db8::2"))
2311 it.publicKey = publicKey
2312 }
2313 val packetReader = TapPacketReader(Handler(handlerThread.looper),
2314 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
2315 packetReader.startAsyncForTest()
2316 handlerThread.waitForIdle(TIMEOUT_MS)
2317
2318 val registrationRecord = NsdRegistrationRecord()
2319 tryTest {
2320 registerService(registrationRecord, si)
2321
2322 val announcement = packetReader.pollForReply("$customHostname.local", TYPE_KEY)
2323 assertNotNull(announcement)
2324 val keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
2325 assertEquals(1, keyRecords.size)
2326 val actualRecord = keyRecords.get(0)
2327 assertEquals(TYPE_KEY, actualRecord.nsType)
2328 assertEquals("$customHostname.local", actualRecord.dName)
2329 assertEquals(NAME_RECORDS_TTL_MILLIS, actualRecord.ttl)
2330 assertArrayEquals(publicKey, actualRecord.rr)
2331
2332 // This test case focuses on key announcement so we don't check the details of the
2333 // announcement of the custom host addresses.
2334 val addressRecords = announcement.records[ANSECTION].filter {
2335 it.nsType == DnsResolver.TYPE_AAAA ||
2336 it.nsType == DnsResolver.TYPE_A
2337 }
2338 assertEquals(3, addressRecords.size)
2339 } cleanup {
2340 nsdManager.unregisterService(registrationRecord)
2341 }
2342 }
2343
2344 @Test
2345 fun testAdvertising_registerTwoServicesWithSameCustomHostAndPublicKey_keyAnnounced() {
2346 val si1 = NsdServiceInfo().also {
2347 it.network = testNetwork1.network
2348 it.serviceType = serviceType
2349 it.serviceName = serviceName
2350 it.port = TEST_PORT
2351 it.hostname = customHostname
2352 it.hostAddresses = listOf(
2353 parseNumericAddress("192.0.2.23"),
2354 parseNumericAddress("2001:db8::1"),
2355 parseNumericAddress("2001:db8::2"))
2356 it.publicKey = publicKey
2357 }
2358 val si2 = NsdServiceInfo().also {
2359 it.network = testNetwork1.network
2360 it.serviceType = serviceType2
2361 it.serviceName = serviceName2
2362 it.port = TEST_PORT + 1
2363 it.hostname = customHostname
2364 it.hostAddresses = listOf()
2365 it.publicKey = publicKey
2366 }
2367 val packetReader = TapPacketReader(Handler(handlerThread.looper),
2368 testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
2369 packetReader.startAsyncForTest()
2370 handlerThread.waitForIdle(TIMEOUT_MS)
2371
2372 val registrationRecord1 = NsdRegistrationRecord()
2373 val registrationRecord2 = NsdRegistrationRecord()
2374 tryTest {
2375 registerService(registrationRecord1, si1)
2376
2377 var announcement =
2378 packetReader.pollForReply("$serviceName.$serviceType.local", TYPE_KEY)
2379 assertNotNull(announcement)
2380 var keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
2381 assertEquals(2, keyRecords.size)
2382 assertTrue(keyRecords.any { it.dName == "$serviceName.$serviceType.local" })
2383 assertTrue(keyRecords.any { it.dName == "$customHostname.local" })
2384 assertTrue(keyRecords.all { it.ttl == NAME_RECORDS_TTL_MILLIS })
2385 assertTrue(keyRecords.all { it.rr.contentEquals(publicKey) })
2386
2387 // This test case focuses on key announcement so we don't check the details of the
2388 // announcement of the custom host addresses.
2389 val addressRecords = announcement.records[ANSECTION].filter {
2390 it.nsType == DnsResolver.TYPE_AAAA ||
2391 it.nsType == DnsResolver.TYPE_A
2392 }
2393 assertEquals(3, addressRecords.size)
2394
2395 registerService(registrationRecord2, si2)
2396
2397 announcement = packetReader.pollForReply("$serviceName2.$serviceType2.local", TYPE_KEY)
2398 assertNotNull(announcement)
2399 keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
2400 assertEquals(2, keyRecords.size)
2401 assertTrue(keyRecords.any { it.dName == "$serviceName2.$serviceType2.local" })
2402 assertTrue(keyRecords.any { it.dName == "$customHostname.local" })
2403 assertTrue(keyRecords.all { it.ttl == NAME_RECORDS_TTL_MILLIS })
2404 assertTrue(keyRecords.all { it.rr.contentEquals(publicKey) })
2405 } cleanup {
2406 nsdManager.unregisterService(registrationRecord1)
2407 nsdManager.unregisterService(registrationRecord2)
2408 }
2409 }
2410
2411 @Test
2412 fun testServiceTypeClientRemovedAfterSocketDestroyed() {
2413 val si = makeTestServiceInfo(testNetwork1.network)
2414 // Register service on testNetwork1
2415 val registrationRecord = NsdRegistrationRecord()
2416 registerService(registrationRecord, si)
2417 // Register multiple discovery requests.
2418 val discoveryRecord1 = NsdDiscoveryRecord()
2419 val discoveryRecord2 = NsdDiscoveryRecord()
2420 val discoveryRecord3 = NsdDiscoveryRecord()
2421 nsdManager.discoverServices("_test1._tcp", NsdManager.PROTOCOL_DNS_SD,
2422 testNetwork1.network, { it.run() }, discoveryRecord1)
2423 nsdManager.discoverServices("_test2._tcp", NsdManager.PROTOCOL_DNS_SD,
2424 testNetwork1.network, { it.run() }, discoveryRecord2)
2425 nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord3)
2426
2427 tryTest {
2428 discoveryRecord1.expectCallback<DiscoveryStarted>()
2429 discoveryRecord2.expectCallback<DiscoveryStarted>()
2430 discoveryRecord3.expectCallback<DiscoveryStarted>()
2431 val foundInfo = discoveryRecord3.waitForServiceDiscovered(
2432 serviceName, serviceType, testNetwork1.network)
2433 assertEquals(testNetwork1.network, foundInfo.network)
2434 // Verify that associated ServiceTypeClients has been created for testNetwork1.
2435 assertTrue("No serviceTypeClients for testNetwork1.",
2436 hasServiceTypeClientsForNetwork(
2437 getServiceTypeClients(), testNetwork1.network))
2438
2439 // Disconnect testNetwork1
2440 runAsShell(MANAGE_TEST_NETWORKS) {
2441 testNetwork1.close(cm)
2442 }
2443
2444 // Verify that no ServiceTypeClients for testNetwork1.
2445 discoveryRecord3.expectCallback<ServiceLost>()
2446 assertFalse("Still has serviceTypeClients for testNetwork1.",
2447 hasServiceTypeClientsForNetwork(
2448 getServiceTypeClients(), testNetwork1.network))
2449 } cleanupStep {
2450 nsdManager.stopServiceDiscovery(discoveryRecord1)
2451 nsdManager.stopServiceDiscovery(discoveryRecord2)
2452 nsdManager.stopServiceDiscovery(discoveryRecord3)
2453 discoveryRecord1.expectCallback<DiscoveryStopped>()
2454 discoveryRecord2.expectCallback<DiscoveryStopped>()
2455 discoveryRecord3.expectCallback<DiscoveryStopped>()
2456 } cleanup {
2457 nsdManager.unregisterService(registrationRecord)
2458 registrationRecord.expectCallback<ServiceUnregistered>()
2459 }
2460 }
2461
2462 private fun hasServiceTypeClientsForNetwork(clients: List<String>, network: Network): Boolean {
2463 return clients.any { client -> client.substring(
2464 client.indexOf("network=") + "network=".length,
2465 client.indexOf("interfaceIndex=") - 1) == network.getNetId().toString()
2466 }
2467 }
2468
2469 /**
2470 * Get ServiceTypeClient logs from the system dump servicediscovery section.
2471 *
2472 * The sample output:
2473 * ServiceTypeClient: Type{_nmt079019787._tcp.local} \
2474 * SocketKey{ network=116 interfaceIndex=68 } with 1 listeners.
2475 * ServiceTypeClient: Type{_nmt079019787._tcp.local} \
2476 * SocketKey{ network=115 interfaceIndex=67 } with 1 listeners.
2477 */
2478 private fun getServiceTypeClients(): List<String> {
2479 return SystemUtil.runShellCommand(
2480 InstrumentationRegistry.getInstrumentation(), "dumpsys servicediscovery")
2481 .split("\n").mapNotNull { line ->
2482 line.indexOf("ServiceTypeClient:").let { idx ->
2483 if (idx == -1) null
2484 else line.substring(idx)
2485 }
2486 }
2487 }
2488
2489 private fun buildConflictingAnnouncement(): ByteBuffer {
2490 /*
2491 Generated with:
2492 scapy.raw(scapy.DNS(rd=0, qr=1, aa=1, qd = None, an =
2493 scapy.DNSRRSRV(rrname='NsdTest123456789._nmt123456789._tcp.local',
2494 rclass=0x8001, port=31234, target='conflict.local', ttl=120)
2495 )).hex()
2496 */
2497 val mdnsPayload = HexDump.hexStringToByteArray("000084000000000100000000104e736454657" +
2498 "3743132333435363738390d5f6e6d74313233343536373839045f746370056c6f63616c00002" +
2499 "18001000000780016000000007a0208636f6e666c696374056c6f63616c00")
2500 replaceServiceNameAndTypeWithTestSuffix(mdnsPayload)
2501
2502 return buildMdnsPacket(mdnsPayload)
2503 }
2504
2505 private fun buildConflictingAnnouncementForCustomHost(): ByteBuffer {
2506 /*
2507 Generated with scapy:
2508 raw(DNS(rd=0, qr=1, aa=1, qd = None, an =
2509 DNSRR(rrname='NsdTestHost123456789.local', type=28, rclass=1, ttl=120,
2510 rdata='2001:db8::321')
2511 )).hex()
2512 */
2513 val mdnsPayload = HexDump.hexStringToByteArray("000084000000000100000000144e7364" +
2514 "54657374486f7374313233343536373839056c6f63616c00001c000100000078001020010db80000" +
2515 "00000000000000000321")
2516 replaceCustomHostnameWithTestSuffix(mdnsPayload)
2517
2518 return buildMdnsPacket(mdnsPayload)
2519 }
2520
2521 /**
2522 * Replaces occurrences of "NsdTest123456789" and "_nmt123456789" in mDNS payload with the
2523 * actual random name and type that are used by the test.
2524 */
2525 private fun replaceServiceNameAndTypeWithTestSuffix(mdnsPayload: ByteArray) {
2526 // Test service name and types have consistent length and are always ASCII
2527 val testPacketName = "NsdTest123456789".encodeToByteArray()
2528 val testPacketTypePrefix = "_nmt123456789".encodeToByteArray()
2529 val encodedServiceName = serviceName.encodeToByteArray()
2530 val encodedTypePrefix = serviceType.split('.')[0].encodeToByteArray()
2531
2532 val packetBuffer = ByteBuffer.wrap(mdnsPayload)
2533 replaceAll(packetBuffer, testPacketName, encodedServiceName)
2534 replaceAll(packetBuffer, testPacketTypePrefix, encodedTypePrefix)
2535 }
2536
2537 /**
2538 * Replaces occurrences of "NsdTestHost123456789" in mDNS payload with the
2539 * actual random host name that are used by the test.
2540 */
2541 private fun replaceCustomHostnameWithTestSuffix(mdnsPayload: ByteArray) {
2542 // Test custom hostnames have consistent length and are always ASCII
2543 val testPacketName = "NsdTestHost123456789".encodeToByteArray()
2544 val encodedHostname = customHostname.encodeToByteArray()
2545
2546 val packetBuffer = ByteBuffer.wrap(mdnsPayload)
2547 replaceAll(packetBuffer, testPacketName, encodedHostname)
2548 }
2549
2550 private tailrec fun replaceAll(buffer: ByteBuffer, source: ByteArray, replacement: ByteArray) {
2551 assertEquals(source.size, replacement.size)
2552 val index = buffer.array().indexOf(source)
2553 if (index < 0) return
2554
2555 val origPosition = buffer.position()
2556 buffer.position(index)
2557 buffer.put(replacement)
2558 buffer.position(origPosition)
2559 replaceAll(buffer, source, replacement)
2560 }
2561
2562 private fun buildMdnsPacket(
2563 mdnsPayload: ByteArray,
2564 srcAddr: Inet6Address = testSrcAddr
2565 ): ByteBuffer {
2566 val packetBuffer = PacketBuilder.allocate(true /* hasEther */, IPPROTO_IPV6,
2567 IPPROTO_UDP, mdnsPayload.size)
2568 val packetBuilder = PacketBuilder(packetBuffer)
2569 // Multicast ethernet address for IPv6 to ff02::fb
2570 val multicastEthAddr = MacAddress.fromBytes(
2571 byteArrayOf(0x33, 0x33, 0, 0, 0, 0xfb.toByte()))
2572 packetBuilder.writeL2Header(
2573 MacAddress.fromBytes(byteArrayOf(1, 2, 3, 4, 5, 6)) /* srcMac */,
2574 multicastEthAddr,
2575 ETH_P_IPV6.toShort())
2576 packetBuilder.writeIpv6Header(
2577 0x60000000, // version=6, traffic class=0x0, flowlabel=0x0
2578 IPPROTO_UDP.toByte(),
2579 64 /* hop limit */,
2580 srcAddr,
2581 multicastIpv6Addr /* dstIp */)
2582 packetBuilder.writeUdpHeader(MDNS_PORT /* srcPort */, MDNS_PORT /* dstPort */)
2583 packetBuffer.put(mdnsPayload)
2584 return packetBuilder.finalizePacket()
2585 }
2586
2587 /**
2588 * Register a service and return its registration record.
2589 */
2590 private fun registerService(
2591 record: NsdRegistrationRecord,
2592 si: NsdServiceInfo,
2593 executor: Executor = Executor { it.run() }
2594 ): NsdServiceInfo {
2595 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, executor, record)
2596 // We may not always get the name that we tried to register;
2597 // This events tells us the name that was registered.
2598 val cb = record.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
2599 return cb.serviceInfo
2600 }
2601
2602 /**
2603 * Update a service.
2604 */
2605 private fun updateService(
2606 record: NsdRegistrationRecord,
2607 si: NsdServiceInfo,
2608 executor: Executor = Executor { it.run() }
2609 ) {
2610 nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, executor, record)
2611 // TODO: add the callback check for the update.
2612 }
2613
2614 private fun resolveService(discoveredInfo: NsdServiceInfo): NsdServiceInfo {
2615 val record = NsdResolveRecord()
2616 nsdManager.resolveService(discoveredInfo, Executor { it.run() }, record)
2617 val resolvedCb = record.expectCallback<ServiceResolved>()
2618 assertEquals(discoveredInfo.serviceName, resolvedCb.serviceInfo.serviceName)
2619
2620 return resolvedCb.serviceInfo
2621 }
2622 }
2623
ByteArraynull2624 private fun ByteArray.indexOf(sub: ByteArray): Int {
2625 var subIndex = 0
2626 forEachIndexed { i, b ->
2627 when (b) {
2628 // Still matching: continue comparing with next byte
2629 sub[subIndex] -> {
2630 subIndex++
2631 if (subIndex == sub.size) {
2632 return i - sub.size + 1
2633 }
2634 }
2635 // Not matching next byte but matches first byte: continue comparing with 2nd byte
2636 sub[0] -> subIndex = 1
2637 // No matches: continue comparing from first byte
2638 else -> subIndex = 0
2639 }
2640 }
2641 return -1
2642 }
2643
utf8ToStringnull2644 private fun ByteArray?.utf8ToString(): String {
2645 if (this == null) return ""
2646 return String(this, StandardCharsets.UTF_8)
2647 }
2648
assertAddressEqualsnull2649 private fun assertAddressEquals(expected: List<InetAddress>, actual: List<InetAddress>) {
2650 // No duplicate addresses in the actual address list
2651 assertEquals(actual.toSet().size, actual.size)
2652 assertEquals(expected.toSet(), actual.toSet())
2653 }
2654