[mdns] add API for discovery with subtype
This commit adds support of discovering services with explicit subtype.
With this change, a service can be discovered with subtype with:
```
nsdManager.discoverServices(
new DiscoveryRequest.Builder("_http._tcp", PROTOCOL_DNS_SD)
.setSubtype("_printer").build(),
executor, listener);
```
Bug: 265095929
Test: atest CtsNetTestCases FrameworksNetTests
Change-Id: Iba76283a003cf2d52a8c26e1de872c3e8e433350
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 3d53d6c..f0e0ae8 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -54,6 +54,7 @@
"junit",
"junit-params",
"modules-utils-build",
+ "net-tests-utils",
"net-utils-framework-common",
"truth",
"TetheringIntegrationTestsBaseLib",
diff --git a/tests/cts/net/src/android/net/cts/DiscoveryRequestTest.kt b/tests/cts/net/src/android/net/cts/DiscoveryRequestTest.kt
new file mode 100644
index 0000000..909a5bc
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/DiscoveryRequestTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts
+
+import android.net.Network
+import android.net.nsd.DiscoveryRequest
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.assertThrows
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for {@link DiscoveryRequest}. */
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@ConnectivityModuleTest
+class DiscoveryRequestTest {
+ @Test
+ fun testParcelingIsLossLess() {
+ val requestWithNullFields =
+ DiscoveryRequest.Builder("_ipps._tcp").build()
+ val requestWithAllFields =
+ DiscoveryRequest.Builder("_ipps._tcp")
+ .setSubtype("_xyz")
+ .setNetwork(Network(1))
+ .build()
+
+ assertParcelingIsLossless(requestWithNullFields)
+ assertParcelingIsLossless(requestWithAllFields)
+ }
+
+ @Test
+ fun testBuilder_success() {
+ val request = DiscoveryRequest.Builder("_ipps._tcp")
+ .setSubtype("_xyz")
+ .setNetwork(Network(1))
+ .build()
+
+ assertEquals("_ipps._tcp", request.serviceType)
+ assertEquals("_xyz", request.subtype)
+ assertEquals(Network(1), request.network)
+ }
+
+ @Test
+ fun testBuilderConstructor_emptyServiceType_throwsIllegalArgument() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DiscoveryRequest.Builder("")
+ }
+ }
+
+ @Test
+ fun testEquality() {
+ val request1 = DiscoveryRequest.Builder("_ipps._tcp").build()
+ val request2 = DiscoveryRequest.Builder("_ipps._tcp").build()
+ val request3 = DiscoveryRequest.Builder("_ipps._tcp")
+ .setSubtype("_xyz")
+ .setNetwork(Network(1))
+ .build()
+ val request4 = DiscoveryRequest.Builder("_ipps._tcp")
+ .setSubtype("_xyz")
+ .setNetwork(Network(1))
+ .build()
+
+ assertEquals(request1, request2)
+ assertEquals(request3, request4)
+ assertNotEquals(request1, request3)
+ assertNotEquals(request2, request4)
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index e433442..8f9f8c7 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -53,6 +53,7 @@
import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdatedLost
import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.UnregisterCallbackSucceeded
import android.net.cts.util.CtsNetUtils
+import android.net.nsd.DiscoveryRequest
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.net.nsd.OffloadEngine
@@ -113,6 +114,7 @@
import kotlin.math.min
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
+import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.fail
@@ -1042,9 +1044,11 @@
nsdManager.discoverServices("_subtype1.$serviceType",
NsdManager.PROTOCOL_DNS_SD,
testNetwork1.network, Executor { it.run() }, subtype1DiscoveryRecord)
- nsdManager.discoverServices("_subtype2.$serviceType",
- NsdManager.PROTOCOL_DNS_SD,
- testNetwork1.network, Executor { it.run() }, subtype2DiscoveryRecord)
+
+ nsdManager.discoverServices(
+ DiscoveryRequest.Builder(serviceType).setSubtype("_subtype2")
+ .setNetwork(testNetwork1.network).build(),
+ Executor { it.run() }, subtype2DiscoveryRecord)
val info1 = subtype1DiscoveryRecord.waitForServiceDiscovered(
serviceName, serviceType, testNetwork1.network)
@@ -1099,6 +1103,28 @@
}
@Test
+ fun testSubtypeDiscovery_typeMatchButSubtypeNotMatch_notDiscovered() {
+ val si1 = makeTestServiceInfo(network = testNetwork1.network).apply {
+ serviceType += ",_subtype1"
+ }
+ val registrationRecord = NsdRegistrationRecord()
+ val subtype2DiscoveryRecord = NsdDiscoveryRecord()
+ tryTest {
+ registerService(registrationRecord, si1)
+ val request = DiscoveryRequest.Builder(serviceType)
+ .setSubtype("_subtype2").setNetwork(testNetwork1.network).build()
+ nsdManager.discoverServices(request, { it.run() }, subtype2DiscoveryRecord)
+ subtype2DiscoveryRecord.expectCallback<DiscoveryStarted>()
+ subtype2DiscoveryRecord.assertNoCallback(timeoutMs = 2000)
+ } cleanupStep {
+ nsdManager.stopServiceDiscovery(subtype2DiscoveryRecord)
+ subtype2DiscoveryRecord.expectCallback<DiscoveryStopped>()
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
+ @Test
fun testSubtypeAdvertising_tooManySubtypes_returnsFailureBadParameters() {
val si = makeTestServiceInfo(network = testNetwork1.network)
// Sets 101 subtypes in total
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 461ead8..aabe8d3 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -285,6 +285,7 @@
private void doTestDiscoverService() throws Exception {
NsdManager manager = mManager;
+ DiscoveryRequest request1 = new DiscoveryRequest.Builder("a_type").build();
NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type");
NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type");
@@ -305,7 +306,7 @@
int key2 = getRequestKey(req ->
verify(mServiceConn, times(2)).discoverServices(req.capture(), any()));
- mCallback.onDiscoverServicesStarted(key2, reply1);
+ mCallback.onDiscoverServicesStarted(key2, request1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
@@ -345,7 +346,7 @@
int key3 = getRequestKey(req ->
verify(mServiceConn, times(3)).discoverServices(req.capture(), any()));
- mCallback.onDiscoverServicesStarted(key3, reply1);
+ mCallback.onDiscoverServicesStarted(key3, request1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
// Client unregisters immediately, it fails