[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:
parent
7b2643f65f
commit
97d6aeb3a2
10 changed files with 497 additions and 37 deletions
|
|
@ -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);
|
||||
|
|
|
|||
247
framework-t/src/android/net/nsd/DiscoveryRequest.java
Normal file
247
framework-t/src/android/net/nsd/DiscoveryRequest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
19
framework/aidl-export/android/net/nsd/DiscoveryRequest.aidl
Normal file
19
framework/aidl-export/android/net/nsd/DiscoveryRequest.aidl
Normal 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;
|
||||
|
|
@ -54,6 +54,7 @@ java_defaults {
|
|||
"junit",
|
||||
"junit-params",
|
||||
"modules-utils-build",
|
||||
"net-tests-utils",
|
||||
"net-utils-framework-common",
|
||||
"truth",
|
||||
"TetheringIntegrationTestsBaseLib",
|
||||
|
|
|
|||
90
tests/cts/net/src/android/net/cts/DiscoveryRequestTest.kt
Normal file
90
tests/cts/net/src/android/net/cts/DiscoveryRequestTest.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue