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