[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
This commit is contained in:
Kangping Dong 2023-12-08 15:51:09 +08:00
commit 97d6aeb3a2
10 changed files with 497 additions and 37 deletions

View file

@ -210,9 +210,26 @@ package android.net {
package android.net.nsd {
@FlaggedApi("com.android.net.flags.nsd_subtypes_support_enabled") public final class DiscoveryRequest implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.net.Network getNetwork();
method @NonNull public String getServiceType();
method @Nullable public String getSubtype();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.nsd.DiscoveryRequest> CREATOR;
}
public static final class DiscoveryRequest.Builder {
ctor public DiscoveryRequest.Builder(@NonNull String);
method @NonNull public android.net.nsd.DiscoveryRequest build();
method @NonNull public android.net.nsd.DiscoveryRequest.Builder setNetwork(@Nullable android.net.Network);
method @NonNull public android.net.nsd.DiscoveryRequest.Builder setSubtype(@Nullable String);
}
public final class NsdManager {
method public void discoverServices(String, int, android.net.nsd.NsdManager.DiscoveryListener);
method public void discoverServices(@NonNull String, int, @Nullable android.net.Network, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
method @FlaggedApi("com.android.net.flags.nsd_subtypes_support_enabled") public void discoverServices(@NonNull android.net.nsd.DiscoveryRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void discoverServices(@NonNull String, int, @NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
method public void registerService(@NonNull android.net.nsd.NsdServiceInfo, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.RegistrationListener);

View file

@ -0,0 +1,247 @@
/*
* 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.nsd;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.Objects;
/**
* Encapsulates parameters for {@link NsdManager#discoverServices}.
*/
@FlaggedApi(NsdManager.Flags.NSD_SUBTYPES_SUPPORT_ENABLED)
public final class DiscoveryRequest implements Parcelable {
private final int mProtocolType;
@NonNull
private final String mServiceType;
@Nullable
private final String mSubtype;
@Nullable
private final Network mNetwork;
// TODO: add mDiscoveryConfig for more fine-grained discovery behavior control
@NonNull
public static final Creator<DiscoveryRequest> CREATOR =
new Creator<>() {
@Override
public DiscoveryRequest createFromParcel(Parcel in) {
int protocolType = in.readInt();
String serviceType = in.readString();
String subtype = in.readString();
Network network =
in.readParcelable(Network.class.getClassLoader(), Network.class);
return new DiscoveryRequest(protocolType, serviceType, subtype, network);
}
@Override
public DiscoveryRequest[] newArray(int size) {
return new DiscoveryRequest[size];
}
};
private DiscoveryRequest(int protocolType, @NonNull String serviceType,
@Nullable String subtype, @Nullable Network network) {
mProtocolType = protocolType;
mServiceType = serviceType;
mSubtype = subtype;
mNetwork = network;
}
/**
* Returns the service type in format of dot-joint string of two labels.
*
* For example, "_ipp._tcp" for internet printer and "_matter._tcp" for <a
* href="https://csa-iot.org/all-solutions/matter">Matter</a> operational device.
*/
@NonNull
public String getServiceType() {
return mServiceType;
}
/**
* Returns the subtype without the trailing "._sub" label or {@code null} if no subtype is
* specified.
*
* For example, the return value will be "_printer" for subtype "_printer._sub".
*/
@Nullable
public String getSubtype() {
return mSubtype;
}
/**
* Returns the service discovery protocol.
*
* @hide
*/
public int getProtocolType() {
return mProtocolType;
}
/**
* Returns the {@link Network} on which the query should be sent or {@code null} if no
* network is specified.
*/
@Nullable
public Network getNetwork() {
return mNetwork;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(", protocolType: ").append(mProtocolType)
.append(", serviceType: ").append(mServiceType)
.append(", subtype: ").append(mSubtype)
.append(", network: ").append(mNetwork);
return sb.toString();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof DiscoveryRequest)) {
return false;
} else {
DiscoveryRequest otherRequest = (DiscoveryRequest) other;
return mProtocolType == otherRequest.mProtocolType
&& Objects.equals(mServiceType, otherRequest.mServiceType)
&& Objects.equals(mSubtype, otherRequest.mSubtype)
&& Objects.equals(mNetwork, otherRequest.mNetwork);
}
}
@Override
public int hashCode() {
return Objects.hash(mProtocolType, mServiceType, mSubtype, mNetwork);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mProtocolType);
dest.writeString(mServiceType);
dest.writeString(mSubtype);
dest.writeParcelable(mNetwork, flags);
}
/** The builder for creating new {@link DiscoveryRequest} objects. */
public static final class Builder {
private final int mProtocolType;
@NonNull
private String mServiceType;
@Nullable
private String mSubtype;
@Nullable
private Network mNetwork;
/**
* Creates a new default {@link Builder} object with given service type.
*
* @throws IllegalArgumentException if {@code serviceType} is {@code null} or an empty
* string
*/
public Builder(@NonNull String serviceType) {
this(NsdManager.PROTOCOL_DNS_SD, serviceType);
}
/** @hide */
public Builder(int protocolType, @NonNull String serviceType) {
NsdManager.checkProtocol(protocolType);
mProtocolType = protocolType;
setServiceType(serviceType);
}
/**
* Sets the service type to be discovered or {@code null} if no services should be queried.
*
* The {@code serviceType} must be a dot-joint string of two labels. For example,
* "_ipp._tcp" for internet printer. Additionally, the first label must start with
* underscore ('_') and the second label must be either "_udp" or "_tcp". Otherwise, {@link
* NsdManager#discoverServices} will fail with {@link NsdManager#FAILURE_BAD_PARAMETER}.
*
* @throws IllegalArgumentException if {@code serviceType} is {@code null} or an empty
* string
*
* @hide
*/
@NonNull
public Builder setServiceType(@NonNull String serviceType) {
if (TextUtils.isEmpty(serviceType)) {
throw new IllegalArgumentException("Service type cannot be empty");
}
mServiceType = serviceType;
return this;
}
/**
* Sets the optional subtype of the services to be discovered.
*
* If a non-empty {@code subtype} is specified, it must start with underscore ('_') and
* have the trailing "._sub" removed. Otherwise, {@link NsdManager#discoverServices} will
* fail with {@link NsdManager#FAILURE_BAD_PARAMETER}. For example, {@code subtype} should
* be "_printer" for DNS name "_printer._sub._http._tcp". In this case, only services with
* this {@code subtype} will be queried, rather than all services of the base service type.
*
* Note that a non-empty service type must be specified with {@link #setServiceType} if a
* non-empty subtype is specified by this method.
*/
@NonNull
public Builder setSubtype(@Nullable String subtype) {
mSubtype = subtype;
return this;
}
/**
* Sets the {@link Network} on which the discovery queries should be sent.
*
* @param network the discovery network or {@code null} if the query should be sent on
* all supported networks
*/
@NonNull
public Builder setNetwork(@Nullable Network network) {
mNetwork = network;
return this;
}
/**
* Creates a new {@link DiscoveryRequest} object.
*/
@NonNull
public DiscoveryRequest build() {
return new DiscoveryRequest(mProtocolType, mServiceType, mSubtype, mNetwork);
}
}
}

View file

@ -17,6 +17,7 @@
package android.net.nsd;
import android.os.Messenger;
import android.net.nsd.DiscoveryRequest;
import android.net.nsd.NsdServiceInfo;
/**
@ -24,7 +25,7 @@ import android.net.nsd.NsdServiceInfo;
* @hide
*/
oneway interface INsdManagerCallback {
void onDiscoverServicesStarted(int listenerKey, in NsdServiceInfo info);
void onDiscoverServicesStarted(int listenerKey, in DiscoveryRequest discoveryRequest);
void onDiscoverServicesFailed(int listenerKey, int error);
void onServiceFound(int listenerKey, in NsdServiceInfo info);
void onServiceLost(int listenerKey, in NsdServiceInfo info);

View file

@ -17,6 +17,7 @@
package android.net.nsd;
import android.net.nsd.AdvertisingRequest;
import android.net.nsd.DiscoveryRequest;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.IOffloadEngine;
import android.net.nsd.NsdServiceInfo;
@ -30,7 +31,7 @@ import android.os.Messenger;
interface INsdServiceConnector {
void registerService(int listenerKey, in AdvertisingRequest advertisingRequest);
void unregisterService(int listenerKey);
void discoverServices(int listenerKey, in NsdServiceInfo serviceInfo);
void discoverServices(int listenerKey, in DiscoveryRequest discoveryRequest);
void stopDiscovery(int listenerKey);
void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
void startDaemon();
@ -39,4 +40,4 @@ interface INsdServiceConnector {
void unregisterServiceInfoCallback(int listenerKey);
void registerOffloadEngine(String ifaceName, in IOffloadEngine cb, long offloadCapabilities, long offloadType);
void unregisterOffloadEngine(in IOffloadEngine cb);
}
}

View file

@ -57,6 +57,7 @@ import com.android.net.module.util.CollectionUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
@ -360,6 +361,8 @@ public final class NsdManager {
@GuardedBy("mMapLock")
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
@GuardedBy("mMapLock")
private final SparseArray<DiscoveryRequest> mDiscoveryMap = new SparseArray<>();
@GuardedBy("mMapLock")
private final SparseArray<Executor> mExecutorMap = new SparseArray<>();
private final Object mMapLock = new Object();
// Map of listener key sent by client -> per-network discovery tracker
@ -715,6 +718,12 @@ public final class NsdManager {
mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info));
}
private void sendDiscoveryRequest(
int message, int listenerKey, DiscoveryRequest discoveryRequest) {
mServHandler.sendMessage(
mServHandler.obtainMessage(message, 0, listenerKey, discoveryRequest));
}
private void sendError(int message, int listenerKey, int error) {
mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey));
}
@ -724,8 +733,8 @@ public final class NsdManager {
}
@Override
public void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
sendInfo(DISCOVER_SERVICES_STARTED, listenerKey, info);
public void onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest) {
sendDiscoveryRequest(DISCOVER_SERVICES_STARTED, listenerKey, discoveryRequest);
}
@Override
@ -1003,10 +1012,12 @@ public final class NsdManager {
final Object obj = message.obj;
final Object listener;
final NsdServiceInfo ns;
final DiscoveryRequest discoveryRequest;
final Executor executor;
synchronized (mMapLock) {
listener = mListenerMap.get(key);
ns = mServiceMap.get(key);
discoveryRequest = mDiscoveryMap.get(key);
executor = mExecutorMap.get(key);
}
if (listener == null) {
@ -1014,17 +1025,22 @@ public final class NsdManager {
return;
}
if (DBG) {
Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns);
if (discoveryRequest != null) {
Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", discovery "
+ discoveryRequest);
} else {
Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns);
}
}
switch (what) {
case DISCOVER_SERVICES_STARTED:
final String s = getNsdServiceInfoType((NsdServiceInfo) obj);
final String s = getNsdServiceInfoType((DiscoveryRequest) obj);
executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStarted(s));
break;
case DISCOVER_SERVICES_FAILED:
removeListener(key);
executor.execute(() -> ((DiscoveryListener) listener).onStartDiscoveryFailed(
getNsdServiceInfoType(ns), errorCode));
getNsdServiceInfoType(discoveryRequest), errorCode));
break;
case SERVICE_FOUND:
executor.execute(() -> ((DiscoveryListener) listener).onServiceFound(
@ -1039,12 +1055,12 @@ public final class NsdManager {
// the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED
removeListener(key);
executor.execute(() -> ((DiscoveryListener) listener).onStopDiscoveryFailed(
getNsdServiceInfoType(ns), errorCode));
getNsdServiceInfoType(discoveryRequest), errorCode));
break;
case STOP_DISCOVERY_SUCCEEDED:
removeListener(key);
executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStopped(
getNsdServiceInfoType(ns)));
getNsdServiceInfoType(discoveryRequest)));
break;
case REGISTER_SERVICE_FAILED:
removeListener(key);
@ -1117,21 +1133,33 @@ public final class NsdManager {
return mListenerKey;
}
// Assert that the listener is not in the map, then add it and returns its key
private int putListener(Object listener, Executor e, NsdServiceInfo s) {
checkListener(listener);
final int key;
private int putListener(Object listener, Executor e, NsdServiceInfo serviceInfo) {
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
return putListener(listener, e, mServiceMap, serviceInfo);
}
}
private int putListener(Object listener, Executor e, DiscoveryRequest discoveryRequest) {
synchronized (mMapLock) {
return putListener(listener, e, mDiscoveryMap, discoveryRequest);
}
}
// Assert that the listener is not in the map, then add it and returns its key
private <T> int putListener(Object listener, Executor e, SparseArray<T> map, T value) {
synchronized (mMapLock) {
checkListener(listener);
final int key;
final int valueIndex = mListenerMap.indexOfValue(listener);
if (valueIndex != -1) {
throw new IllegalArgumentException("listener already in use");
}
key = nextListenerKey();
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
map.put(key, value);
mExecutorMap.put(key, e);
return key;
}
return key;
}
private int updateRegisteredListener(Object listener, Executor e, NsdServiceInfo s) {
@ -1148,6 +1176,7 @@ public final class NsdManager {
synchronized (mMapLock) {
mListenerMap.remove(key);
mServiceMap.remove(key);
mDiscoveryMap.remove(key);
mExecutorMap.remove(key);
}
}
@ -1163,9 +1192,9 @@ public final class NsdManager {
}
}
private static String getNsdServiceInfoType(NsdServiceInfo s) {
if (s == null) return "?";
return s.getServiceType();
private static String getNsdServiceInfoType(DiscoveryRequest r) {
if (r == null) return "?";
return r.getServiceType();
}
/**
@ -1406,15 +1435,44 @@ public final class NsdManager {
if (TextUtils.isEmpty(serviceType)) {
throw new IllegalArgumentException("Service type cannot be empty");
}
checkProtocol(protocolType);
DiscoveryRequest request = new DiscoveryRequest.Builder(protocolType, serviceType)
.setNetwork(network).build();
discoverServices(request, executor, listener);
}
NsdServiceInfo s = new NsdServiceInfo();
s.setServiceType(serviceType);
s.setNetwork(network);
int key = putListener(listener, executor, s);
/**
* Initiates service discovery to browse for instances of a service type. Service discovery
* consumes network bandwidth and will continue until the application calls
* {@link #stopServiceDiscovery}.
*
* <p> The function call immediately returns after sending a request to start service
* discovery to the framework. The application is notified of a success to initiate
* discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure
* through {@link DiscoveryListener#onStartDiscoveryFailed}.
*
* <p> Upon successful start, application is notified when a service is found with
* {@link DiscoveryListener#onServiceFound} or when a service is lost with
* {@link DiscoveryListener#onServiceLost}.
*
* <p> Upon failure to start, service discovery is not active and application does
* not need to invoke {@link #stopServiceDiscovery}
*
* <p> The application should call {@link #stopServiceDiscovery} when discovery of this
* service type is no longer required, and/or whenever the application is paused or
* stopped.
*
* @param discoveryRequest the {@link DiscoveryRequest} object which specifies the discovery
* parameters such as service type, subtype and network
* @param executor Executor to run listener callbacks with
* @param listener The listener notifies of a successful discovery and is used
* to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
*/
@FlaggedApi(Flags.NSD_SUBTYPES_SUPPORT_ENABLED)
public void discoverServices(@NonNull DiscoveryRequest discoveryRequest,
@NonNull Executor executor, @NonNull DiscoveryListener listener) {
int key = putListener(listener, executor, discoveryRequest);
try {
mService.discoverServices(key, s);
mService.discoverServices(key, discoveryRequest);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@ -1465,12 +1523,10 @@ public final class NsdManager {
throw new IllegalArgumentException("Service type cannot be empty");
}
Objects.requireNonNull(networkRequest, "NetworkRequest cannot be null");
checkProtocol(protocolType);
DiscoveryRequest discoveryRequest =
new DiscoveryRequest.Builder(protocolType, serviceType).build();
NsdServiceInfo s = new NsdServiceInfo();
s.setServiceType(serviceType);
final int baseListenerKey = putListener(listener, executor, s);
final int baseListenerKey = putListener(listener, executor, discoveryRequest);
final PerNetworkDiscoveryTracker discoveryInfo = new PerNetworkDiscoveryTracker(
serviceType, protocolType, executor, listener);
@ -1602,6 +1658,7 @@ public final class NsdManager {
* @param executor Executor to run callbacks with
* @param listener to receive callback upon service update
*/
// TODO: use {@link DiscoveryRequest} to specify the service to be subscribed
public void registerServiceInfoCallback(@NonNull NsdServiceInfo serviceInfo,
@NonNull Executor executor, @NonNull ServiceInfoCallback listener) {
checkServiceInfo(serviceInfo);
@ -1643,7 +1700,7 @@ public final class NsdManager {
Objects.requireNonNull(listener, "listener cannot be null");
}
private static void checkProtocol(int protocolType) {
static void checkProtocol(int protocolType) {
if (protocolType != PROTOCOL_DNS_SD) {
throw new IllegalArgumentException("Unsupported protocol");
}

View file

@ -0,0 +1,19 @@
/*
* 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.nsd;
@JavaOnlyStableParcelable parcelable DiscoveryRequest;

View file

@ -54,6 +54,7 @@ java_defaults {
"junit",
"junit-params",
"modules-utils-build",
"net-tests-utils",
"net-utils-framework-common",
"truth",
"TetheringIntegrationTestsBaseLib",

View file

@ -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)
}
}

View file

@ -53,6 +53,7 @@ import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.Ser
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 java.util.concurrent.Executor
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 @@ class NsdManagerTest {
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)
@ -1098,6 +1102,28 @@ class NsdManagerTest {
}
}
@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)

View file

@ -285,6 +285,7 @@ public class NsdManagerTest {
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 @@ public class NsdManagerTest {
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 @@ public class NsdManagerTest {
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