Merge changes from topic "mdns-subtypes" into main
* changes: [mdns] add service-side impl for subtypes advertising [mdns] add support for advertising subtypes
This commit is contained in:
commit
19d055514c
14 changed files with 426 additions and 187 deletions
|
|
@ -275,6 +275,7 @@ package android.net.nsd {
|
|||
method public int getPort();
|
||||
method public String getServiceName();
|
||||
method public String getServiceType();
|
||||
method @FlaggedApi("com.android.net.flags.nsd_subtypes_support_enabled") @NonNull public java.util.Set<java.lang.String> getSubtypes();
|
||||
method public void removeAttribute(String);
|
||||
method public void setAttribute(String, String);
|
||||
method @Deprecated public void setHost(java.net.InetAddress);
|
||||
|
|
@ -283,6 +284,7 @@ package android.net.nsd {
|
|||
method public void setPort(int);
|
||||
method public void setServiceName(String);
|
||||
method public void setServiceType(String);
|
||||
method @FlaggedApi("com.android.net.flags.nsd_subtypes_support_enabled") public void setSubtypes(@NonNull java.util.Set<java.lang.String>);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.net.nsd.NsdServiceInfo> CREATOR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@ public final class NsdManager {
|
|||
public static class Flags {
|
||||
static final String REGISTER_NSD_OFFLOAD_ENGINE_API =
|
||||
"com.android.net.flags.register_nsd_offload_engine_api";
|
||||
static final String NSD_SUBTYPES_SUPPORT_ENABLED =
|
||||
"com.android.net.flags.nsd_subtypes_support_enabled";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package android.net.nsd;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import android.annotation.FlaggedApi;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
|
|
@ -24,6 +27,7 @@ import android.os.Parcel;
|
|||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.net.module.util.InetAddressUtils;
|
||||
|
|
@ -35,6 +39,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A class representing service information for network service discovery
|
||||
|
|
@ -48,9 +53,11 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
|
||||
private String mServiceType;
|
||||
|
||||
private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<>();
|
||||
private final Set<String> mSubtypes;
|
||||
|
||||
private final List<InetAddress> mHostAddresses = new ArrayList<>();
|
||||
private final ArrayMap<String, byte[]> mTxtRecord;
|
||||
|
||||
private final List<InetAddress> mHostAddresses;
|
||||
|
||||
private int mPort;
|
||||
|
||||
|
|
@ -60,14 +67,34 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
private int mInterfaceIndex;
|
||||
|
||||
public NsdServiceInfo() {
|
||||
mSubtypes = new ArraySet<>();
|
||||
mTxtRecord = new ArrayMap<>();
|
||||
mHostAddresses = new ArrayList<>();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public NsdServiceInfo(String sn, String rt) {
|
||||
this();
|
||||
mServiceName = sn;
|
||||
mServiceType = rt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of {@code other}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public NsdServiceInfo(@NonNull NsdServiceInfo other) {
|
||||
mServiceName = other.getServiceName();
|
||||
mServiceType = other.getServiceType();
|
||||
mSubtypes = new ArraySet<>(other.getSubtypes());
|
||||
mTxtRecord = new ArrayMap<>(other.mTxtRecord);
|
||||
mHostAddresses = new ArrayList<>(other.getHostAddresses());
|
||||
mPort = other.getPort();
|
||||
mNetwork = other.getNetwork();
|
||||
mInterfaceIndex = other.getInterfaceIndex();
|
||||
}
|
||||
|
||||
/** Get the service name */
|
||||
public String getServiceName() {
|
||||
return mServiceName;
|
||||
|
|
@ -391,11 +418,41 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
mInterfaceIndex = interfaceIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subtypes to be advertised for this service instance.
|
||||
*
|
||||
* The elements in {@code subtypes} should be the subtype identifiers which have the trailing
|
||||
* "._sub" removed. For example, the subtype should be "_printer" for
|
||||
* "_printer._sub._http._tcp.local".
|
||||
*
|
||||
* Only one subtype will be registered if multiple elements of {@code subtypes} have the same
|
||||
* case-insensitive value.
|
||||
*/
|
||||
@FlaggedApi(NsdManager.Flags.NSD_SUBTYPES_SUPPORT_ENABLED)
|
||||
public void setSubtypes(@NonNull Set<String> subtypes) {
|
||||
mSubtypes.clear();
|
||||
mSubtypes.addAll(subtypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns subtypes of this service instance.
|
||||
*
|
||||
* When this object is returned by the service discovery/browse APIs (etc. {@link
|
||||
* NsdManager.DiscoveryListener}), the return value may or may not include the subtypes of this
|
||||
* service.
|
||||
*/
|
||||
@FlaggedApi(NsdManager.Flags.NSD_SUBTYPES_SUPPORT_ENABLED)
|
||||
@NonNull
|
||||
public Set<String> getSubtypes() {
|
||||
return Collections.unmodifiableSet(mSubtypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("name: ").append(mServiceName)
|
||||
.append(", type: ").append(mServiceType)
|
||||
.append(", subtypes: ").append(TextUtils.join(", ", mSubtypes))
|
||||
.append(", hostAddresses: ").append(TextUtils.join(", ", mHostAddresses))
|
||||
.append(", port: ").append(mPort)
|
||||
.append(", network: ").append(mNetwork);
|
||||
|
|
@ -414,6 +471,7 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mServiceName);
|
||||
dest.writeString(mServiceType);
|
||||
dest.writeStringList(new ArrayList<>(mSubtypes));
|
||||
dest.writeInt(mPort);
|
||||
|
||||
// TXT record key/value pairs.
|
||||
|
|
@ -445,6 +503,7 @@ public final class NsdServiceInfo implements Parcelable {
|
|||
NsdServiceInfo info = new NsdServiceInfo();
|
||||
info.mServiceName = in.readString();
|
||||
info.mServiceType = in.readString();
|
||||
info.setSubtypes(new ArraySet<>(in.createStringArrayList()));
|
||||
info.mPort = in.readInt();
|
||||
|
||||
// TXT record key/value pairs.
|
||||
|
|
|
|||
|
|
@ -111,7 +111,9 @@ import java.net.SocketException;
|
|||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
|
@ -170,6 +172,8 @@ public class NsdService extends INsdManager.Stub {
|
|||
"mdns_advertiser_allowlist_";
|
||||
private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
|
||||
|
||||
private static final String TYPE_SUBTYPE_LABEL_REGEX = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]";
|
||||
|
||||
@VisibleForTesting
|
||||
static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
|
||||
"mdns_config_running_app_active_importance_cutoff";
|
||||
|
|
@ -186,6 +190,7 @@ public class NsdService extends INsdManager.Stub {
|
|||
static final int NO_TRANSACTION = -1;
|
||||
private static final int NO_SENT_QUERY_COUNT = 0;
|
||||
private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
|
||||
private static final int MAX_SUBTYPE_COUNT = 100;
|
||||
private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
|
||||
|
||||
private final Context mContext;
|
||||
|
|
@ -688,6 +693,35 @@ public class NsdService extends INsdManager.Stub {
|
|||
return mClients.get(args.connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code false} if {@code subtypes} exceeds the maximum number limit or
|
||||
* contains invalid subtype label.
|
||||
*/
|
||||
private boolean checkSubtypeLabels(Set<String> subtypes) {
|
||||
if (subtypes.size() > MAX_SUBTYPE_COUNT) {
|
||||
mServiceLogs.e(
|
||||
"Too many subtypes: " + subtypes.size() + " (max = "
|
||||
+ MAX_SUBTYPE_COUNT + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String subtype : subtypes) {
|
||||
if (!checkSubtypeLabel(subtype)) {
|
||||
mServiceLogs.e("Subtype " + subtype + " is invalid");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Set<String> dedupSubtypeLabels(Collection<String> subtypes) {
|
||||
final Map<String, String> subtypeMap = new LinkedHashMap<>(subtypes.size());
|
||||
for (String subtype : subtypes) {
|
||||
subtypeMap.put(MdnsUtils.toDnsLowerCase(subtype), subtype);
|
||||
}
|
||||
return new ArraySet<>(subtypeMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Message msg) {
|
||||
final ClientInfo clientInfo;
|
||||
|
|
@ -846,13 +880,23 @@ public class NsdService extends INsdManager.Stub {
|
|||
serviceInfo.setServiceName(truncateServiceName(
|
||||
serviceInfo.getServiceName()));
|
||||
|
||||
Set<String> subtypes = new ArraySet<>(serviceInfo.getSubtypes());
|
||||
if (!TextUtils.isEmpty(typeSubtype.second)) {
|
||||
subtypes.add(typeSubtype.second);
|
||||
}
|
||||
|
||||
subtypes = dedupSubtypeLabels(subtypes);
|
||||
|
||||
if (!checkSubtypeLabels(subtypes)) {
|
||||
clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
|
||||
NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
|
||||
break;
|
||||
}
|
||||
|
||||
serviceInfo.setSubtypes(subtypes);
|
||||
|
||||
maybeStartMonitoringSockets();
|
||||
// TODO: pass in the subtype as well. Including the subtype in the
|
||||
// service type would generate service instance names like
|
||||
// Name._subtype._sub._type._tcp, which is incorrect
|
||||
// (it should be Name._type._tcp).
|
||||
mAdvertiser.addOrUpdateService(transactionId, serviceInfo,
|
||||
typeSubtype.second,
|
||||
MdnsAdvertisingOptions.newBuilder().build());
|
||||
storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
|
||||
serviceInfo.getNetwork());
|
||||
|
|
@ -1388,6 +1432,7 @@ public class NsdService extends INsdManager.Stub {
|
|||
servInfo,
|
||||
network == null ? INetd.LOCAL_NET_ID : network.netId,
|
||||
serviceInfo.getInterfaceIndex());
|
||||
servInfo.setSubtypes(dedupSubtypeLabels(serviceInfo.getSubtypes()));
|
||||
return servInfo;
|
||||
}
|
||||
|
||||
|
|
@ -1591,18 +1636,17 @@ public class NsdService extends INsdManager.Stub {
|
|||
public static Pair<String, String> parseTypeAndSubtype(String serviceType) {
|
||||
if (TextUtils.isEmpty(serviceType)) return null;
|
||||
|
||||
final String typeOrSubtypePattern = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]";
|
||||
final Pattern serviceTypePattern = Pattern.compile(
|
||||
// Optional leading subtype (_subtype._type._tcp)
|
||||
// (?: xxx) is a non-capturing parenthesis, don't capture the dot
|
||||
"^(?:(" + typeOrSubtypePattern + ")\\.)?"
|
||||
"^(?:(" + TYPE_SUBTYPE_LABEL_REGEX + ")\\.)?"
|
||||
// Actual type (_type._tcp.local)
|
||||
+ "(" + typeOrSubtypePattern + "\\._(?:tcp|udp))"
|
||||
+ "(" + TYPE_SUBTYPE_LABEL_REGEX + "\\._(?:tcp|udp))"
|
||||
// Drop '.' at the end of service type that is compatible with old backend.
|
||||
// e.g. allow "_type._tcp.local."
|
||||
+ "\\.?"
|
||||
// Optional subtype after comma, for "_type._tcp,_subtype" format
|
||||
+ "(?:,(" + typeOrSubtypePattern + "))?"
|
||||
+ "(?:,(" + TYPE_SUBTYPE_LABEL_REGEX + "))?"
|
||||
+ "$");
|
||||
final Matcher matcher = serviceTypePattern.matcher(serviceType);
|
||||
if (!matcher.matches()) return null;
|
||||
|
|
@ -1611,6 +1655,11 @@ public class NsdService extends INsdManager.Stub {
|
|||
return new Pair<>(matcher.group(2), subtype);
|
||||
}
|
||||
|
||||
/** Returns {@code true} if {@code subtype} is a valid DNS-SD subtype label. */
|
||||
private static boolean checkSubtypeLabel(String subtype) {
|
||||
return Pattern.compile("^" + TYPE_SUBTYPE_LABEL_REGEX + "$").matcher(subtype).matches();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
|
||||
this(ctx, handler, cleanupDelayMs, new Dependencies());
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
|
|
@ -351,8 +352,7 @@ public class MdnsAdvertiser {
|
|||
mPendingRegistrations.put(id, registration);
|
||||
for (int i = 0; i < mAdvertisers.size(); i++) {
|
||||
try {
|
||||
mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo(),
|
||||
registration.getSubtype());
|
||||
mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo());
|
||||
} catch (NameConflictException e) {
|
||||
mSharedLog.wtf("Name conflict adding services that should have unique names",
|
||||
e);
|
||||
|
|
@ -367,7 +367,8 @@ public class MdnsAdvertiser {
|
|||
void updateService(int id, @NonNull Registration registration) {
|
||||
mPendingRegistrations.put(id, registration);
|
||||
for (int i = 0; i < mAdvertisers.size(); i++) {
|
||||
mAdvertisers.valueAt(i).updateService(id, registration.getSubtype());
|
||||
mAdvertisers.valueAt(i).updateService(
|
||||
id, registration.getServiceInfo().getSubtypes());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +418,7 @@ public class MdnsAdvertiser {
|
|||
final Registration registration = mPendingRegistrations.valueAt(i);
|
||||
try {
|
||||
advertiser.addService(mPendingRegistrations.keyAt(i),
|
||||
registration.getServiceInfo(), registration.getSubtype());
|
||||
registration.getServiceInfo());
|
||||
} catch (NameConflictException e) {
|
||||
mSharedLog.wtf("Name conflict adding services that should have unique names",
|
||||
e);
|
||||
|
|
@ -485,16 +486,12 @@ public class MdnsAdvertiser {
|
|||
private int mConflictCount;
|
||||
@NonNull
|
||||
private NsdServiceInfo mServiceInfo;
|
||||
@Nullable
|
||||
private String mSubtype;
|
||||
|
||||
int mConflictDuringProbingCount;
|
||||
int mConflictAfterProbingCount;
|
||||
|
||||
private Registration(@NonNull NsdServiceInfo serviceInfo, @Nullable String subtype) {
|
||||
private Registration(@NonNull NsdServiceInfo serviceInfo) {
|
||||
this.mOriginalName = serviceInfo.getServiceName();
|
||||
this.mServiceInfo = serviceInfo;
|
||||
this.mSubtype = subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -507,10 +504,11 @@ public class MdnsAdvertiser {
|
|||
}
|
||||
|
||||
/**
|
||||
* Update subType for the registration.
|
||||
* Update subTypes for the registration.
|
||||
*/
|
||||
public void updateSubtype(@Nullable String subtype) {
|
||||
this.mSubtype = subtype;
|
||||
public void updateSubtypes(@NonNull Set<String> subtypes) {
|
||||
mServiceInfo = new NsdServiceInfo(mServiceInfo);
|
||||
mServiceInfo.setSubtypes(subtypes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -540,17 +538,8 @@ public class MdnsAdvertiser {
|
|||
// In case of conflict choose a different service name. After the first conflict use
|
||||
// "Name (2)", then "Name (3)" etc.
|
||||
// TODO: use a hidden method in NsdServiceInfo once MdnsAdvertiser is moved to service-t
|
||||
final NsdServiceInfo newInfo = new NsdServiceInfo();
|
||||
final NsdServiceInfo newInfo = new NsdServiceInfo(mServiceInfo);
|
||||
newInfo.setServiceName(getUpdatedServiceName(renameCount));
|
||||
newInfo.setServiceType(mServiceInfo.getServiceType());
|
||||
for (Map.Entry<String, byte[]> attr : mServiceInfo.getAttributes().entrySet()) {
|
||||
newInfo.setAttribute(attr.getKey(),
|
||||
attr.getValue() == null ? null : new String(attr.getValue()));
|
||||
}
|
||||
newInfo.setHost(mServiceInfo.getHost());
|
||||
newInfo.setPort(mServiceInfo.getPort());
|
||||
newInfo.setNetwork(mServiceInfo.getNetwork());
|
||||
// interfaceIndex is not set when registering
|
||||
return newInfo;
|
||||
}
|
||||
|
||||
|
|
@ -565,11 +554,6 @@ public class MdnsAdvertiser {
|
|||
public NsdServiceInfo getServiceInfo() {
|
||||
return mServiceInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSubtype() {
|
||||
return mSubtype;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -665,14 +649,14 @@ public class MdnsAdvertiser {
|
|||
*
|
||||
* @param id A unique ID for the service.
|
||||
* @param service The service info to advertise.
|
||||
* @param subtype An optional subtype to advertise the service with.
|
||||
* @param advertisingOptions The advertising options.
|
||||
*/
|
||||
public void addOrUpdateService(int id, NsdServiceInfo service, @Nullable String subtype,
|
||||
public void addOrUpdateService(int id, NsdServiceInfo service,
|
||||
MdnsAdvertisingOptions advertisingOptions) {
|
||||
checkThread();
|
||||
final Registration existingRegistration = mRegistrations.get(id);
|
||||
final Network network = service.getNetwork();
|
||||
final Set<String> subtypes = service.getSubtypes();
|
||||
Registration registration;
|
||||
if (advertisingOptions.isOnlyUpdate()) {
|
||||
if (existingRegistration == null) {
|
||||
|
|
@ -687,10 +671,10 @@ public class MdnsAdvertiser {
|
|||
return;
|
||||
|
||||
}
|
||||
mSharedLog.i("Update service " + service + " with ID " + id + " and subtype " + subtype
|
||||
+ " advertisingOptions " + advertisingOptions);
|
||||
mSharedLog.i("Update service " + service + " with ID " + id + " and subtypes "
|
||||
+ subtypes + " advertisingOptions " + advertisingOptions);
|
||||
registration = existingRegistration;
|
||||
registration.updateSubtype(subtype);
|
||||
registration.updateSubtypes(subtypes);
|
||||
} else {
|
||||
if (existingRegistration != null) {
|
||||
mSharedLog.e("Adding duplicate registration for " + service);
|
||||
|
|
@ -698,9 +682,9 @@ public class MdnsAdvertiser {
|
|||
mCb.onRegisterServiceFailed(id, NsdManager.FAILURE_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
mSharedLog.i("Adding service " + service + " with ID " + id + " and subtype " + subtype
|
||||
+ " advertisingOptions " + advertisingOptions);
|
||||
registration = new Registration(service, subtype);
|
||||
mSharedLog.i("Adding service " + service + " with ID " + id + " and subtypes "
|
||||
+ subtypes + " advertisingOptions " + advertisingOptions);
|
||||
registration = new Registration(service);
|
||||
final BiPredicate<Network, InterfaceAdvertiserRequest> checkConflictFilter;
|
||||
if (network == null) {
|
||||
// If registering on all networks, no advertiser must have conflicts
|
||||
|
|
@ -793,15 +777,10 @@ public class MdnsAdvertiser {
|
|||
private OffloadServiceInfoWrapper createOffloadService(int serviceId,
|
||||
@NonNull Registration registration, byte[] rawOffloadPacket) {
|
||||
final NsdServiceInfo nsdServiceInfo = registration.getServiceInfo();
|
||||
final List<String> subTypes = new ArrayList<>();
|
||||
String subType = registration.getSubtype();
|
||||
if (subType != null) {
|
||||
subTypes.add(subType);
|
||||
}
|
||||
final OffloadServiceInfo offloadServiceInfo = new OffloadServiceInfo(
|
||||
new OffloadServiceInfo.Key(nsdServiceInfo.getServiceName(),
|
||||
nsdServiceInfo.getServiceType()),
|
||||
subTypes,
|
||||
new ArrayList<>(nsdServiceInfo.getSubtypes()),
|
||||
String.join(".", mDeviceHostName),
|
||||
rawOffloadPacket,
|
||||
// TODO: define overlayable resources in
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.android.server.connectivity.mdns.util.MdnsUtils;
|
|||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A class that handles advertising services on a {@link MdnsInterfaceSocket} tied to an interface.
|
||||
|
|
@ -232,12 +233,12 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
|||
* Update an already registered service without sending exit/re-announcement packet.
|
||||
*
|
||||
* @param id An exiting service id
|
||||
* @param subtype A new subtype
|
||||
* @param subtypes New subtypes
|
||||
*/
|
||||
public void updateService(int id, @Nullable String subtype) {
|
||||
public void updateService(int id, @NonNull Set<String> subtypes) {
|
||||
// The current implementation is intended to be used in cases where subtypes don't get
|
||||
// announced.
|
||||
mRecordRepository.updateService(id, subtype);
|
||||
mRecordRepository.updateService(id, subtypes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -245,9 +246,8 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
|||
*
|
||||
* @throws NameConflictException There is already a service being advertised with that name.
|
||||
*/
|
||||
public void addService(int id, NsdServiceInfo service, @Nullable String subtype)
|
||||
throws NameConflictException {
|
||||
final int replacedExitingService = mRecordRepository.addService(id, service, subtype);
|
||||
public void addService(int id, NsdServiceInfo service) throws NameConflictException {
|
||||
final int replacedExitingService = mRecordRepository.addService(id, service);
|
||||
// Cancel announcements for the existing service. This only happens for exiting services
|
||||
// (so cancelling exiting announcements), as per RecordRepository.addService.
|
||||
if (replacedExitingService >= 0) {
|
||||
|
|
|
|||
|
|
@ -74,8 +74,6 @@ public class MdnsRecordRepository {
|
|||
|
||||
// Top-level domain for link-local queries, as per RFC6762 3.
|
||||
private static final String LOCAL_TLD = "local";
|
||||
// Subtype separator as per RFC6763 7.1 (_printer._sub._http._tcp.local)
|
||||
private static final String SUBTYPE_SEPARATOR = "_sub";
|
||||
|
||||
// Service type for service enumeration (RFC6763 9.)
|
||||
private static final String[] DNS_SD_SERVICE_TYPE =
|
||||
|
|
@ -161,8 +159,6 @@ public class MdnsRecordRepository {
|
|||
public final RecordInfo<MdnsTextRecord> txtRecord;
|
||||
@NonNull
|
||||
public final NsdServiceInfo serviceInfo;
|
||||
@Nullable
|
||||
public final String subtype;
|
||||
|
||||
/**
|
||||
* Whether the service is sending exit announcements and will be destroyed soon.
|
||||
|
|
@ -185,28 +181,28 @@ public class MdnsRecordRepository {
|
|||
private boolean isProbing;
|
||||
|
||||
/**
|
||||
* Create a ServiceRegistration with only update the subType
|
||||
* Create a ServiceRegistration with only update the subType.
|
||||
*/
|
||||
ServiceRegistration withSubtype(String newSubType) {
|
||||
return new ServiceRegistration(srvRecord.record.getServiceHost(), serviceInfo,
|
||||
newSubType, repliedServiceCount, sentPacketCount, exiting, isProbing);
|
||||
ServiceRegistration withSubtypes(@NonNull Set<String> newSubtypes) {
|
||||
NsdServiceInfo newServiceInfo = new NsdServiceInfo(serviceInfo);
|
||||
newServiceInfo.setSubtypes(newSubtypes);
|
||||
return new ServiceRegistration(srvRecord.record.getServiceHost(), newServiceInfo,
|
||||
repliedServiceCount, sentPacketCount, exiting, isProbing);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a ServiceRegistration for dns-sd service registration (RFC6763).
|
||||
*/
|
||||
ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
|
||||
@Nullable String subtype, int repliedServiceCount, int sentPacketCount,
|
||||
boolean exiting, boolean isProbing) {
|
||||
int repliedServiceCount, int sentPacketCount, boolean exiting, boolean isProbing) {
|
||||
this.serviceInfo = serviceInfo;
|
||||
this.subtype = subtype;
|
||||
|
||||
final String[] serviceType = splitServiceType(serviceInfo);
|
||||
final String[] serviceName = splitFullyQualifiedName(serviceInfo, serviceType);
|
||||
|
||||
// Service PTR record
|
||||
final RecordInfo<MdnsPointerRecord> ptrRecord = new RecordInfo<>(
|
||||
// Service PTR records
|
||||
ptrRecords = new ArrayList<>(serviceInfo.getSubtypes().size() + 1);
|
||||
ptrRecords.add(new RecordInfo<>(
|
||||
serviceInfo,
|
||||
new MdnsPointerRecord(
|
||||
serviceType,
|
||||
|
|
@ -214,26 +210,17 @@ public class MdnsRecordRepository {
|
|||
false /* cacheFlush */,
|
||||
NON_NAME_RECORDS_TTL_MILLIS,
|
||||
serviceName),
|
||||
true /* sharedName */);
|
||||
|
||||
if (subtype == null) {
|
||||
this.ptrRecords = Collections.singletonList(ptrRecord);
|
||||
} else {
|
||||
final String[] subtypeName = new String[serviceType.length + 2];
|
||||
System.arraycopy(serviceType, 0, subtypeName, 2, serviceType.length);
|
||||
subtypeName[0] = subtype;
|
||||
subtypeName[1] = SUBTYPE_SEPARATOR;
|
||||
final RecordInfo<MdnsPointerRecord> subtypeRecord = new RecordInfo<>(
|
||||
serviceInfo,
|
||||
new MdnsPointerRecord(
|
||||
subtypeName,
|
||||
0L /* receiptTimeMillis */,
|
||||
false /* cacheFlush */,
|
||||
NON_NAME_RECORDS_TTL_MILLIS,
|
||||
serviceName),
|
||||
true /* sharedName */);
|
||||
|
||||
this.ptrRecords = List.of(ptrRecord, subtypeRecord);
|
||||
true /* sharedName */));
|
||||
for (String subtype : serviceInfo.getSubtypes()) {
|
||||
ptrRecords.add(new RecordInfo<>(
|
||||
serviceInfo,
|
||||
new MdnsPointerRecord(
|
||||
MdnsUtils.constructFullSubtype(serviceType, subtype),
|
||||
0L /* receiptTimeMillis */,
|
||||
false /* cacheFlush */,
|
||||
NON_NAME_RECORDS_TTL_MILLIS,
|
||||
serviceName),
|
||||
true /* sharedName */));
|
||||
}
|
||||
|
||||
srvRecord = new RecordInfo<>(
|
||||
|
|
@ -284,8 +271,8 @@ public class MdnsRecordRepository {
|
|||
* @param serviceInfo Service to advertise
|
||||
*/
|
||||
ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
|
||||
@Nullable String subtype, int repliedServiceCount, int sentPacketCount) {
|
||||
this(deviceHostname, serviceInfo, subtype, repliedServiceCount, sentPacketCount,
|
||||
int repliedServiceCount, int sentPacketCount) {
|
||||
this(deviceHostname, serviceInfo,repliedServiceCount, sentPacketCount,
|
||||
false /* exiting */, true /* isProbing */);
|
||||
}
|
||||
|
||||
|
|
@ -328,17 +315,17 @@ public class MdnsRecordRepository {
|
|||
* Update a service that already registered in the repository.
|
||||
*
|
||||
* @param serviceId An existing service ID.
|
||||
* @param subtype A new subtype
|
||||
* @param subtypes New subtypes
|
||||
* @return
|
||||
*/
|
||||
public void updateService(int serviceId, @Nullable String subtype) {
|
||||
public void updateService(int serviceId, @NonNull Set<String> subtypes) {
|
||||
final ServiceRegistration existingRegistration = mServices.get(serviceId);
|
||||
if (existingRegistration == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Service ID must already exist for an update request: " + serviceId);
|
||||
}
|
||||
final ServiceRegistration updatedRegistration = existingRegistration.withSubtype(
|
||||
subtype);
|
||||
final ServiceRegistration updatedRegistration = existingRegistration.withSubtypes(
|
||||
subtypes);
|
||||
mServices.put(serviceId, updatedRegistration);
|
||||
}
|
||||
|
||||
|
|
@ -352,8 +339,7 @@ public class MdnsRecordRepository {
|
|||
* ID of the replaced service.
|
||||
* @throws NameConflictException There is already a (non-exiting) service using the name.
|
||||
*/
|
||||
public int addService(int serviceId, NsdServiceInfo serviceInfo, @Nullable String subtype)
|
||||
throws NameConflictException {
|
||||
public int addService(int serviceId, NsdServiceInfo serviceInfo) throws NameConflictException {
|
||||
if (mServices.contains(serviceId)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Service ID must not be reused across registrations: " + serviceId);
|
||||
|
|
@ -366,7 +352,7 @@ public class MdnsRecordRepository {
|
|||
}
|
||||
|
||||
final ServiceRegistration registration = new ServiceRegistration(
|
||||
mDeviceHostname, serviceInfo, subtype, NO_PACKET /* repliedServiceCount */,
|
||||
mDeviceHostname, serviceInfo, NO_PACKET /* repliedServiceCount */,
|
||||
NO_PACKET /* sentPacketCount */);
|
||||
mServices.put(serviceId, registration);
|
||||
|
||||
|
|
@ -929,7 +915,7 @@ public class MdnsRecordRepository {
|
|||
if (existing == null) return null;
|
||||
|
||||
final ServiceRegistration newService = new ServiceRegistration(mDeviceHostname, newInfo,
|
||||
existing.subtype, existing.repliedServiceCount, existing.sentPacketCount);
|
||||
existing.repliedServiceCount, existing.sentPacketCount);
|
||||
mServices.put(serviceId, newService);
|
||||
return makeProbingInfo(
|
||||
serviceId, newService.srvRecord.record, makeProbingInetAddressRecords());
|
||||
|
|
|
|||
|
|
@ -233,6 +233,20 @@ public class MdnsUtils {
|
|||
&& mdnsRecord.getRemainingTTL(now) <= mdnsRecord.getTtl() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new full subtype name with given service type and subtype labels.
|
||||
*
|
||||
* For example, given ["_http", "_tcp"] and "_printer", this method returns a new String array
|
||||
* of ["_printer", "_sub", "_http", "_tcp"].
|
||||
*/
|
||||
public static String[] constructFullSubtype(String[] serviceType, String subtype) {
|
||||
String[] fullSubtype = new String[serviceType.length + 2];
|
||||
fullSubtype[0] = subtype;
|
||||
fullSubtype[1] = MdnsConstants.SUBTYPE_LABEL;
|
||||
System.arraycopy(serviceType, 0, fullSubtype, 2, serviceType.length);
|
||||
return fullSubtype;
|
||||
}
|
||||
|
||||
/** A wrapper class of {@link SystemClock} to be mocked in unit tests. */
|
||||
public static class Clock {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package android.net.nsd;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ import java.net.InetAddress;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(DevSdkIgnoreRunner.class)
|
||||
@SmallTest
|
||||
|
|
@ -114,6 +116,7 @@ public class NsdServiceInfoTest {
|
|||
NsdServiceInfo fullInfo = new NsdServiceInfo();
|
||||
fullInfo.setServiceName("kitten");
|
||||
fullInfo.setServiceType("_kitten._tcp");
|
||||
fullInfo.setSubtypes(Set.of("_thread", "_matter"));
|
||||
fullInfo.setPort(4242);
|
||||
fullInfo.setHostAddresses(List.of(IPV4_ADDRESS));
|
||||
fullInfo.setNetwork(new Network(123));
|
||||
|
|
@ -149,7 +152,7 @@ public class NsdServiceInfoTest {
|
|||
assertFalse(attributedInfo.getAttributes().keySet().contains("sticky"));
|
||||
}
|
||||
|
||||
public void checkParcelable(NsdServiceInfo original) {
|
||||
private static void checkParcelable(NsdServiceInfo original) {
|
||||
// Write to parcel.
|
||||
Parcel p = Parcel.obtain();
|
||||
Bundle writer = new Bundle();
|
||||
|
|
@ -179,11 +182,20 @@ public class NsdServiceInfoTest {
|
|||
}
|
||||
}
|
||||
|
||||
public void assertEmptyServiceInfo(NsdServiceInfo shouldBeEmpty) {
|
||||
private static void assertEmptyServiceInfo(NsdServiceInfo shouldBeEmpty) {
|
||||
byte[] txtRecord = shouldBeEmpty.getTxtRecord();
|
||||
if (txtRecord == null || txtRecord.length == 0) {
|
||||
return;
|
||||
}
|
||||
fail("NsdServiceInfo.getTxtRecord did not return null but " + Arrays.toString(txtRecord));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtypesValidSubtypesSuccess() {
|
||||
NsdServiceInfo info = new NsdServiceInfo();
|
||||
|
||||
info.setSubtypes(Set.of("_thread", "_matter"));
|
||||
|
||||
assertEquals(Set.of("_thread", "_matter"), info.getSubtypes());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted
|
|||
import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped
|
||||
import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.ServiceFound
|
||||
import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.ServiceLost
|
||||
import android.net.cts.NsdRegistrationRecord.RegistrationEvent.RegistrationFailed
|
||||
import android.net.cts.NsdRegistrationRecord.RegistrationEvent.ServiceRegistered
|
||||
import android.net.cts.NsdRegistrationRecord.RegistrationEvent.ServiceUnregistered
|
||||
import android.net.cts.NsdResolveRecord.ResolveEvent.ResolutionStopped
|
||||
|
|
@ -991,6 +992,104 @@ class NsdManagerTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubtypeAdvertisingAndDiscovery_withSetSubtypesApi() {
|
||||
runSubtypeAdvertisingAndDiscoveryTest(useLegacySpecifier = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubtypeAdvertisingAndDiscovery_withSetSubtypesApiAndLegacySpecifier() {
|
||||
runSubtypeAdvertisingAndDiscoveryTest(useLegacySpecifier = true)
|
||||
}
|
||||
|
||||
private fun runSubtypeAdvertisingAndDiscoveryTest(useLegacySpecifier: Boolean) {
|
||||
val si = makeTestServiceInfo(network = testNetwork1.network)
|
||||
if (useLegacySpecifier) {
|
||||
si.subtypes = setOf("_subtype1")
|
||||
|
||||
// Test "_type._tcp.local,_subtype" syntax with the registration
|
||||
si.serviceType = si.serviceType + ",_subtype2"
|
||||
} else {
|
||||
si.subtypes = setOf("_subtype1", "_subtype2")
|
||||
}
|
||||
|
||||
val registrationRecord = NsdRegistrationRecord()
|
||||
|
||||
val baseTypeDiscoveryRecord = NsdDiscoveryRecord()
|
||||
val subtype1DiscoveryRecord = NsdDiscoveryRecord()
|
||||
val subtype2DiscoveryRecord = NsdDiscoveryRecord()
|
||||
val otherSubtypeDiscoveryRecord = NsdDiscoveryRecord()
|
||||
tryTest {
|
||||
registerService(registrationRecord, si)
|
||||
|
||||
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
|
||||
testNetwork1.network, Executor { it.run() }, baseTypeDiscoveryRecord)
|
||||
|
||||
// Test "<subtype>._type._tcp.local" syntax with discovery. Note this is not
|
||||
// "<subtype>._sub._type._tcp.local".
|
||||
nsdManager.discoverServices("_othersubtype.$serviceType",
|
||||
NsdManager.PROTOCOL_DNS_SD,
|
||||
testNetwork1.network, Executor { it.run() }, otherSubtypeDiscoveryRecord)
|
||||
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)
|
||||
|
||||
val info1 = subtype1DiscoveryRecord.waitForServiceDiscovered(
|
||||
serviceName, serviceType, testNetwork1.network)
|
||||
assertTrue(info1.subtypes.contains("_subtype1"))
|
||||
val info2 = subtype2DiscoveryRecord.waitForServiceDiscovered(
|
||||
serviceName, serviceType, testNetwork1.network)
|
||||
assertTrue(info2.subtypes.contains("_subtype2"))
|
||||
baseTypeDiscoveryRecord.waitForServiceDiscovered(
|
||||
serviceName, serviceType, testNetwork1.network)
|
||||
otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStarted>()
|
||||
// The subtype callback was registered later but called, no need for an extra delay
|
||||
otherSubtypeDiscoveryRecord.assertNoCallback(timeoutMs = 0)
|
||||
} cleanupStep {
|
||||
nsdManager.stopServiceDiscovery(baseTypeDiscoveryRecord)
|
||||
nsdManager.stopServiceDiscovery(subtype1DiscoveryRecord)
|
||||
nsdManager.stopServiceDiscovery(subtype2DiscoveryRecord)
|
||||
nsdManager.stopServiceDiscovery(otherSubtypeDiscoveryRecord)
|
||||
|
||||
baseTypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
|
||||
subtype1DiscoveryRecord.expectCallback<DiscoveryStopped>()
|
||||
subtype2DiscoveryRecord.expectCallback<DiscoveryStopped>()
|
||||
otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
|
||||
} cleanup {
|
||||
nsdManager.unregisterService(registrationRecord)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubtypeAdvertising_tooManySubtypes_returnsFailureBadParameters() {
|
||||
val si = makeTestServiceInfo(network = testNetwork1.network)
|
||||
// Sets 101 subtypes in total
|
||||
val seq = generateSequence(1) { it + 1}
|
||||
si.subtypes = seq.take(100).toList().map {it -> "_subtype" + it}.toSet()
|
||||
si.serviceType = si.serviceType + ",_subtype"
|
||||
|
||||
val record = NsdRegistrationRecord()
|
||||
nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, Executor { it.run() }, record)
|
||||
|
||||
val failedCb = record.expectCallback<RegistrationFailed>(REGISTRATION_TIMEOUT_MS)
|
||||
assertEquals(NsdManager.FAILURE_BAD_PARAMETERS, failedCb.errorCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubtypeAdvertising_emptySubtypeLabel_returnsFailureBadParameters() {
|
||||
val si = makeTestServiceInfo(network = testNetwork1.network)
|
||||
si.subtypes = setOf("")
|
||||
|
||||
val record = NsdRegistrationRecord()
|
||||
nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, Executor { it.run() }, record)
|
||||
|
||||
val failedCb = record.expectCallback<RegistrationFailed>(REGISTRATION_TIMEOUT_MS)
|
||||
assertEquals(NsdManager.FAILURE_BAD_PARAMETERS, failedCb.errorCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRegisterWithConflictDuringProbing() {
|
||||
// This test requires shims supporting T+ APIs (NsdServiceInfo.network)
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
// TODOs:
|
||||
// - test client can send requests and receive replies
|
||||
|
|
@ -1117,7 +1118,8 @@ public class NsdServiceTest {
|
|||
waitForIdle();
|
||||
verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(s ->
|
||||
"Instance".equals(s.getServiceName())
|
||||
&& SERVICE_TYPE.equals(s.getServiceType())), eq("_subtype"), any());
|
||||
&& SERVICE_TYPE.equals(s.getServiceType())
|
||||
&& s.getSubtypes().equals(Set.of("_subtype"))), any());
|
||||
|
||||
final DiscoveryListener discListener = mock(DiscoveryListener.class);
|
||||
client.discoverServices(typeWithSubtype, PROTOCOL, network, Runnable::run, discListener);
|
||||
|
|
@ -1223,7 +1225,7 @@ public class NsdServiceTest {
|
|||
|
||||
final ArgumentCaptor<Integer> serviceIdCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
verify(mAdvertiser).addOrUpdateService(serviceIdCaptor.capture(),
|
||||
argThat(info -> matches(info, regInfo)), eq(null) /* subtype */, any());
|
||||
argThat(info -> matches(info, regInfo)), any());
|
||||
|
||||
client.unregisterService(regListenerWithoutFeature);
|
||||
waitForIdle();
|
||||
|
|
@ -1283,9 +1285,9 @@ public class NsdServiceTest {
|
|||
|
||||
// The advertiser is enabled for _type2 but not _type1
|
||||
verify(mAdvertiser, never()).addOrUpdateService(anyInt(),
|
||||
argThat(info -> matches(info, service1)), eq(null) /* subtype */, any());
|
||||
argThat(info -> matches(info, service1)), any());
|
||||
verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(info -> matches(info, service2)),
|
||||
eq(null) /* subtype */, any());
|
||||
any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1310,7 +1312,7 @@ public class NsdServiceTest {
|
|||
verify(mSocketProvider).startMonitoringSockets();
|
||||
final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
verify(mAdvertiser).addOrUpdateService(idCaptor.capture(), argThat(info ->
|
||||
matches(info, regInfo)), eq(null) /* subtype */, any());
|
||||
matches(info, regInfo)), any());
|
||||
|
||||
// Verify onServiceRegistered callback
|
||||
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
|
||||
|
|
@ -1358,7 +1360,7 @@ public class NsdServiceTest {
|
|||
|
||||
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
|
||||
waitForIdle();
|
||||
verify(mAdvertiser, never()).addOrUpdateService(anyInt(), any(), any(), any());
|
||||
verify(mAdvertiser, never()).addOrUpdateService(anyInt(), any(), any());
|
||||
|
||||
verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed(
|
||||
argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR));
|
||||
|
|
@ -1388,8 +1390,7 @@ public class NsdServiceTest {
|
|||
final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
// Service name is truncated to 63 characters
|
||||
verify(mAdvertiser).addOrUpdateService(idCaptor.capture(),
|
||||
argThat(info -> info.getServiceName().equals("a".repeat(63))),
|
||||
eq(null) /* subtype */, any());
|
||||
argThat(info -> info.getServiceName().equals("a".repeat(63))), any());
|
||||
|
||||
// Verify onServiceRegistered callback
|
||||
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
|
||||
|
|
@ -1479,7 +1480,7 @@ public class NsdServiceTest {
|
|||
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
|
||||
waitForIdle();
|
||||
verify(mSocketProvider).startMonitoringSockets();
|
||||
verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), any());
|
||||
verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any());
|
||||
|
||||
// Verify the discovery uses MdnsDiscoveryManager
|
||||
final DiscoveryListener discListener = mock(DiscoveryListener.class);
|
||||
|
|
@ -1512,7 +1513,7 @@ public class NsdServiceTest {
|
|||
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
|
||||
waitForIdle();
|
||||
verify(mSocketProvider).startMonitoringSockets();
|
||||
verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), any());
|
||||
verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any());
|
||||
|
||||
final Network wifiNetwork1 = new Network(123);
|
||||
final Network wifiNetwork2 = new Network(124);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ private val TEST_SOCKETKEY_1 = SocketKey(1001 /* interfaceIndex */)
|
|||
private val TEST_SOCKETKEY_2 = SocketKey(1002 /* interfaceIndex */)
|
||||
private val TEST_HOSTNAME = arrayOf("Android_test", "local")
|
||||
private const val TEST_SUBTYPE = "_subtype"
|
||||
private const val TEST_SUBTYPE2 = "_subtype2"
|
||||
private val TEST_INTERFACE1 = "test_iface1"
|
||||
private val TEST_INTERFACE2 = "test_iface2"
|
||||
private val TEST_OFFLOAD_PACKET1 = byteArrayOf(0x01, 0x02, 0x03)
|
||||
|
|
@ -80,6 +81,13 @@ private val SERVICE_1 = NsdServiceInfo("TestServiceName", "_advertisertest._tcp"
|
|||
network = TEST_NETWORK_1
|
||||
}
|
||||
|
||||
private val SERVICE_1_SUBTYPE = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
|
||||
subtypes = setOf(TEST_SUBTYPE)
|
||||
port = 12345
|
||||
hostAddresses = listOf(TEST_ADDR)
|
||||
network = TEST_NETWORK_1
|
||||
}
|
||||
|
||||
private val LONG_SERVICE_1 =
|
||||
NsdServiceInfo("a".repeat(48) + "TestServiceName", "_longadvertisertest._tcp").apply {
|
||||
port = 12345
|
||||
|
|
@ -93,6 +101,14 @@ private val ALL_NETWORKS_SERVICE = NsdServiceInfo("TestServiceName", "_advertise
|
|||
network = null
|
||||
}
|
||||
|
||||
private val ALL_NETWORKS_SERVICE_SUBTYPE =
|
||||
NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
|
||||
subtypes = setOf(TEST_SUBTYPE)
|
||||
port = 12345
|
||||
hostAddresses = listOf(TEST_ADDR)
|
||||
network = null
|
||||
}
|
||||
|
||||
private val ALL_NETWORKS_SERVICE_2 =
|
||||
NsdServiceInfo("TESTSERVICENAME", "_ADVERTISERTEST._tcp").apply {
|
||||
port = 12345
|
||||
|
|
@ -189,7 +205,7 @@ class MdnsAdvertiserTest {
|
|||
val advertiser =
|
||||
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1,
|
||||
null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
|
||||
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
|
||||
verify(socketProvider).requestSocket(eq(TEST_NETWORK_1), socketCbCaptor.capture())
|
||||
|
|
@ -247,14 +263,14 @@ class MdnsAdvertiserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun testAddService_AllNetworks() {
|
||||
fun testAddService_AllNetworksWithSubType() {
|
||||
val advertiser =
|
||||
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE,
|
||||
TEST_SUBTYPE, DEFAULT_ADVERTISING_OPTION) }
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE_SUBTYPE,
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
|
||||
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
|
||||
verify(socketProvider).requestSocket(eq(ALL_NETWORKS_SERVICE.network),
|
||||
verify(socketProvider).requestSocket(eq(ALL_NETWORKS_SERVICE_SUBTYPE.network),
|
||||
socketCbCaptor.capture())
|
||||
|
||||
val socketCb = socketCbCaptor.value
|
||||
|
|
@ -270,9 +286,9 @@ class MdnsAdvertiserTest {
|
|||
eq(thread.looper), any(), intAdvCbCaptor2.capture(), eq(TEST_HOSTNAME), any(), any()
|
||||
)
|
||||
verify(mockInterfaceAdvertiser1).addService(
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE), eq(TEST_SUBTYPE))
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE_SUBTYPE))
|
||||
verify(mockInterfaceAdvertiser2).addService(
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE), eq(TEST_SUBTYPE))
|
||||
anyInt(), eq(ALL_NETWORKS_SERVICE_SUBTYPE))
|
||||
|
||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
||||
postSync { intAdvCbCaptor1.value.onServiceProbingSucceeded(
|
||||
|
|
@ -286,7 +302,7 @@ class MdnsAdvertiserTest {
|
|||
mockInterfaceAdvertiser2, SERVICE_ID_1) }
|
||||
verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE2), eq(OFFLOAD_SERVICEINFO))
|
||||
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1),
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE) })
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE_SUBTYPE) })
|
||||
|
||||
// Services are conflicted.
|
||||
postSync { intAdvCbCaptor1.value.onServiceConflict(mockInterfaceAdvertiser1, SERVICE_ID_1) }
|
||||
|
|
@ -323,7 +339,7 @@ class MdnsAdvertiserTest {
|
|||
val advertiser =
|
||||
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1,
|
||||
null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
|
||||
val oneNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
|
||||
verify(socketProvider).requestSocket(eq(TEST_NETWORK_1), oneNetSocketCbCaptor.capture())
|
||||
|
|
@ -331,18 +347,18 @@ class MdnsAdvertiserTest {
|
|||
|
||||
// Register a service with the same name on all networks (name conflict)
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_2, ALL_NETWORKS_SERVICE,
|
||||
null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
val allNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
|
||||
verify(socketProvider).requestSocket(eq(null), allNetSocketCbCaptor.capture())
|
||||
val allNetSocketCb = allNetSocketCbCaptor.value
|
||||
|
||||
postSync { advertiser.addOrUpdateService(LONG_SERVICE_ID_1, LONG_SERVICE_1,
|
||||
null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
postSync { advertiser.addOrUpdateService(LONG_SERVICE_ID_2, LONG_ALL_NETWORKS_SERVICE,
|
||||
null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
|
||||
postSync { advertiser.addOrUpdateService(CASE_INSENSITIVE_TEST_SERVICE_ID,
|
||||
ALL_NETWORKS_SERVICE_2, null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
ALL_NETWORKS_SERVICE_2, DEFAULT_ADVERTISING_OPTION) }
|
||||
|
||||
// Callbacks for matching network and all networks both get the socket
|
||||
postSync {
|
||||
|
|
@ -378,15 +394,15 @@ class MdnsAdvertiserTest {
|
|||
eq(thread.looper), any(), intAdvCbCaptor.capture(), eq(TEST_HOSTNAME), any(), any()
|
||||
)
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1),
|
||||
argThat { it.matches(SERVICE_1) }, eq(null))
|
||||
argThat { it.matches(SERVICE_1) })
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_2),
|
||||
argThat { it.matches(expectedRenamed) }, eq(null))
|
||||
argThat { it.matches(expectedRenamed) })
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(LONG_SERVICE_ID_1),
|
||||
argThat { it.matches(LONG_SERVICE_1) }, eq(null))
|
||||
argThat { it.matches(LONG_SERVICE_1) })
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(LONG_SERVICE_ID_2),
|
||||
argThat { it.matches(expectedLongRenamed) }, eq(null))
|
||||
argThat { it.matches(expectedLongRenamed) })
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(CASE_INSENSITIVE_TEST_SERVICE_ID),
|
||||
argThat { it.matches(expectedCaseInsensitiveRenamed) }, eq(null))
|
||||
argThat { it.matches(expectedCaseInsensitiveRenamed) })
|
||||
|
||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
||||
postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
|
||||
|
|
@ -411,7 +427,7 @@ class MdnsAdvertiserTest {
|
|||
val advertiser =
|
||||
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE,
|
||||
null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
|
||||
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
|
||||
verify(socketProvider).requestSocket(eq(null), socketCbCaptor.capture())
|
||||
|
|
@ -420,29 +436,28 @@ class MdnsAdvertiserTest {
|
|||
postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_1, mockSocket1, listOf(TEST_LINKADDR)) }
|
||||
|
||||
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1),
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE) }, eq(null))
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE) })
|
||||
|
||||
val updateOptions = MdnsAdvertisingOptions.newBuilder().setIsOnlyUpdate(true).build()
|
||||
|
||||
// Update with serviceId that is not registered yet should fail
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_2, ALL_NETWORKS_SERVICE, TEST_SUBTYPE,
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_2, ALL_NETWORKS_SERVICE_SUBTYPE,
|
||||
updateOptions) }
|
||||
verify(cb).onRegisterServiceFailed(SERVICE_ID_2, NsdManager.FAILURE_INTERNAL_ERROR)
|
||||
|
||||
// Update service with different NsdServiceInfo should fail
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1, TEST_SUBTYPE,
|
||||
updateOptions) }
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1_SUBTYPE, updateOptions) }
|
||||
verify(cb).onRegisterServiceFailed(SERVICE_ID_1, NsdManager.FAILURE_INTERNAL_ERROR)
|
||||
|
||||
// Update service with same NsdServiceInfo but different subType should succeed
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE, TEST_SUBTYPE,
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, ALL_NETWORKS_SERVICE_SUBTYPE,
|
||||
updateOptions) }
|
||||
verify(mockInterfaceAdvertiser1).updateService(eq(SERVICE_ID_1), eq(TEST_SUBTYPE))
|
||||
verify(mockInterfaceAdvertiser1).updateService(eq(SERVICE_ID_1), eq(setOf(TEST_SUBTYPE)))
|
||||
|
||||
// Newly created MdnsInterfaceAdvertiser will get addService() call.
|
||||
postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_2, mockSocket2, listOf(TEST_LINKADDR2)) }
|
||||
verify(mockInterfaceAdvertiser2).addService(eq(SERVICE_ID_1),
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE) }, eq(TEST_SUBTYPE))
|
||||
argThat { it.matches(ALL_NETWORKS_SERVICE_SUBTYPE) })
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -451,7 +466,7 @@ class MdnsAdvertiserTest {
|
|||
MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags)
|
||||
verify(mockDeps, times(1)).generateHostname()
|
||||
postSync { advertiser.addOrUpdateService(SERVICE_ID_1, SERVICE_1,
|
||||
null /* subtype */, DEFAULT_ADVERTISING_OPTION) }
|
||||
DEFAULT_ADVERTISING_OPTION) }
|
||||
postSync { advertiser.removeService(SERVICE_ID_1) }
|
||||
verify(mockDeps, times(2)).generateHostname()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,13 @@ private val TEST_SERVICE_1 = NsdServiceInfo().apply {
|
|||
port = 12345
|
||||
}
|
||||
|
||||
private val TEST_SERVICE_1_SUBTYPE = NsdServiceInfo().apply {
|
||||
subtypes = setOf("_sub")
|
||||
serviceType = "_testservice._tcp"
|
||||
serviceName = "MyTestService"
|
||||
port = 12345
|
||||
}
|
||||
|
||||
@RunWith(DevSdkIgnoreRunner::class)
|
||||
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
|
||||
class MdnsInterfaceAdvertiserTest {
|
||||
|
|
@ -119,7 +126,7 @@ class MdnsInterfaceAdvertiserTest {
|
|||
knownServices.add(inv.getArgument(0))
|
||||
|
||||
-1
|
||||
}.`when`(repository).addService(anyInt(), any(), any())
|
||||
}.`when`(repository).addService(anyInt(), any())
|
||||
doAnswer { inv ->
|
||||
knownServices.remove(inv.getArgument(0))
|
||||
null
|
||||
|
|
@ -277,10 +284,9 @@ class MdnsInterfaceAdvertiserTest {
|
|||
@Test
|
||||
fun testReplaceExitingService() {
|
||||
doReturn(TEST_SERVICE_ID_DUPLICATE).`when`(repository)
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
|
||||
val subType = "_sub"
|
||||
advertiser.addService(TEST_SERVICE_ID_DUPLICATE, TEST_SERVICE_1, subType)
|
||||
verify(repository).addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
advertiser.addService(TEST_SERVICE_ID_DUPLICATE, TEST_SERVICE_1_SUBTYPE)
|
||||
verify(repository).addService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
verify(announcer).stop(TEST_SERVICE_ID_DUPLICATE)
|
||||
verify(prober).startProbing(any())
|
||||
}
|
||||
|
|
@ -288,9 +294,9 @@ class MdnsInterfaceAdvertiserTest {
|
|||
@Test
|
||||
fun testUpdateExistingService() {
|
||||
doReturn(TEST_SERVICE_ID_DUPLICATE).`when`(repository)
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any(), any())
|
||||
val subType = "_sub"
|
||||
advertiser.updateService(TEST_SERVICE_ID_DUPLICATE, subType)
|
||||
.addService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
val subTypes = setOf("_sub")
|
||||
advertiser.updateService(TEST_SERVICE_ID_DUPLICATE, subTypes)
|
||||
verify(repository).updateService(eq(TEST_SERVICE_ID_DUPLICATE), any())
|
||||
verify(announcer, never()).stop(TEST_SERVICE_ID_DUPLICATE)
|
||||
verify(prober, never()).startProbing(any())
|
||||
|
|
@ -302,8 +308,8 @@ class MdnsInterfaceAdvertiserTest {
|
|||
doReturn(serviceId).`when`(testProbingInfo).serviceId
|
||||
doReturn(testProbingInfo).`when`(repository).setServiceProbing(serviceId)
|
||||
|
||||
advertiser.addService(serviceId, serviceInfo, null /* subtype */)
|
||||
verify(repository).addService(serviceId, serviceInfo, null /* subtype */)
|
||||
advertiser.addService(serviceId, serviceInfo)
|
||||
verify(repository).addService(serviceId, serviceInfo)
|
||||
verify(prober).startProbing(testProbingInfo)
|
||||
|
||||
// Simulate probing success: continues to announcing
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.android.server.connectivity.mdns.MdnsRecordRepository.getReverseDnsAd
|
|||
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.util.Collections
|
||||
|
|
@ -46,6 +47,7 @@ private const val TEST_SERVICE_ID_2 = 43
|
|||
private const val TEST_SERVICE_ID_3 = 44
|
||||
private const val TEST_PORT = 12345
|
||||
private const val TEST_SUBTYPE = "_subtype"
|
||||
private const val TEST_SUBTYPE2 = "_subtype2"
|
||||
private val TEST_HOSTNAME = arrayOf("Android_000102030405060708090A0B0C0D0E0F", "local")
|
||||
private val TEST_ADDRESSES = listOf(
|
||||
LinkAddress(parseNumericAddress("192.0.2.111"), 24),
|
||||
|
|
@ -95,8 +97,7 @@ class MdnsRecordRepositoryTest {
|
|||
fun testAddServiceAndProbe() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
assertEquals(0, repository.servicesCount)
|
||||
assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
|
||||
null /* subtype */))
|
||||
assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1))
|
||||
assertEquals(1, repository.servicesCount)
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
|
|
@ -131,10 +132,10 @@ class MdnsRecordRepositoryTest {
|
|||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
assertFailsWith(NameConflictException::class) {
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1)
|
||||
}
|
||||
assertFailsWith(NameConflictException::class) {
|
||||
repository.addService(TEST_SERVICE_ID_3, TEST_SERVICE_3, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_3, TEST_SERVICE_3)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,10 +145,10 @@ class MdnsRecordRepositoryTest {
|
|||
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
|
||||
assertFailsWith(IllegalArgumentException::class) {
|
||||
repository.updateService(TEST_SERVICE_ID_2, null /* subtype */)
|
||||
repository.updateService(TEST_SERVICE_ID_2, emptySet() /* subtype */)
|
||||
}
|
||||
|
||||
repository.updateService(TEST_SERVICE_ID_1, TEST_SUBTYPE)
|
||||
repository.updateService(TEST_SERVICE_ID_1, setOf(TEST_SUBTYPE))
|
||||
|
||||
val queriedName = arrayOf(TEST_SUBTYPE, "_sub", "_testservice", "_tcp", "local")
|
||||
val questions = listOf(MdnsPointerRecord(queriedName, false /* isUnicast */))
|
||||
|
|
@ -175,9 +176,9 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testInvalidReuseOfServiceId() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
assertFailsWith(IllegalArgumentException::class) {
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_2, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +187,7 @@ class MdnsRecordRepositoryTest {
|
|||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
assertFalse(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
|
|
@ -229,9 +230,10 @@ class MdnsRecordRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun testExitAnnouncements_WithSubtype() {
|
||||
fun testExitAnnouncements_WithSubtypes() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, TEST_SUBTYPE)
|
||||
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
|
||||
setOf(TEST_SUBTYPE, TEST_SUBTYPE2))
|
||||
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
|
||||
|
||||
val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
|
||||
|
|
@ -245,7 +247,7 @@ class MdnsRecordRepositoryTest {
|
|||
assertEquals(0, packet.authorityRecords.size)
|
||||
assertEquals(0, packet.additionalRecords.size)
|
||||
|
||||
assertContentEquals(listOf(
|
||||
assertThat(packet.answers).containsExactly(
|
||||
MdnsPointerRecord(
|
||||
arrayOf("_testservice", "_tcp", "local"),
|
||||
0L /* receiptTimeMillis */,
|
||||
|
|
@ -258,7 +260,12 @@ class MdnsRecordRepositoryTest {
|
|||
false /* cacheFlush */,
|
||||
0L /* ttlMillis */,
|
||||
arrayOf("MyTestService", "_testservice", "_tcp", "local")),
|
||||
), packet.answers)
|
||||
MdnsPointerRecord(
|
||||
arrayOf("_subtype2", "_sub", "_testservice", "_tcp", "local"),
|
||||
0L /* receiptTimeMillis */,
|
||||
false /* cacheFlush */,
|
||||
0L /* ttlMillis */,
|
||||
arrayOf("MyTestService", "_testservice", "_tcp", "local")))
|
||||
|
||||
repository.removeService(TEST_SERVICE_ID_1)
|
||||
assertEquals(0, repository.servicesCount)
|
||||
|
|
@ -272,7 +279,7 @@ class MdnsRecordRepositoryTest {
|
|||
repository.exitService(TEST_SERVICE_ID_1)
|
||||
|
||||
assertEquals(TEST_SERVICE_ID_1,
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* subtype */))
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1))
|
||||
assertEquals(1, repository.servicesCount)
|
||||
|
||||
repository.removeService(TEST_SERVICE_ID_2)
|
||||
|
|
@ -283,7 +290,7 @@ class MdnsRecordRepositoryTest {
|
|||
fun testOnProbingSucceeded() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
val announcementInfo = repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
|
||||
TEST_SUBTYPE)
|
||||
setOf(TEST_SUBTYPE, TEST_SUBTYPE2))
|
||||
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
|
||||
val packet = announcementInfo.getPacket(0)
|
||||
|
||||
|
|
@ -294,12 +301,13 @@ class MdnsRecordRepositoryTest {
|
|||
|
||||
val serviceType = arrayOf("_testservice", "_tcp", "local")
|
||||
val serviceSubtype = arrayOf(TEST_SUBTYPE, "_sub", "_testservice", "_tcp", "local")
|
||||
val serviceSubtype2 = arrayOf(TEST_SUBTYPE2, "_sub", "_testservice", "_tcp", "local")
|
||||
val serviceName = arrayOf("MyTestService", "_testservice", "_tcp", "local")
|
||||
val v4AddrRev = getReverseDnsAddress(TEST_ADDRESSES[0].address)
|
||||
val v6Addr1Rev = getReverseDnsAddress(TEST_ADDRESSES[1].address)
|
||||
val v6Addr2Rev = getReverseDnsAddress(TEST_ADDRESSES[2].address)
|
||||
|
||||
assertContentEquals(listOf(
|
||||
assertThat(packet.answers).containsExactly(
|
||||
// Reverse address and address records for the hostname
|
||||
MdnsPointerRecord(v4AddrRev,
|
||||
0L /* receiptTimeMillis */,
|
||||
|
|
@ -346,6 +354,13 @@ class MdnsRecordRepositoryTest {
|
|||
false /* cacheFlush */,
|
||||
4500000L /* ttlMillis */,
|
||||
serviceName),
|
||||
MdnsPointerRecord(
|
||||
serviceSubtype2,
|
||||
0L /* receiptTimeMillis */,
|
||||
// Not a unique name owned by the announcer, so cacheFlush=false
|
||||
false /* cacheFlush */,
|
||||
4500000L /* ttlMillis */,
|
||||
serviceName),
|
||||
MdnsServiceRecord(
|
||||
serviceName,
|
||||
0L /* receiptTimeMillis */,
|
||||
|
|
@ -367,8 +382,7 @@ class MdnsRecordRepositoryTest {
|
|||
0L /* receiptTimeMillis */,
|
||||
false /* cacheFlush */,
|
||||
4500000L /* ttlMillis */,
|
||||
serviceType)
|
||||
), packet.answers)
|
||||
serviceType))
|
||||
|
||||
assertContentEquals(listOf(
|
||||
MdnsNsecRecord(v4AddrRev,
|
||||
|
|
@ -484,19 +498,20 @@ class MdnsRecordRepositoryTest {
|
|||
|
||||
@Test
|
||||
fun testGetReply() {
|
||||
doGetReplyTest(subtype = null)
|
||||
doGetReplyTest(queryWithSubtype = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetReply_WithSubtype() {
|
||||
doGetReplyTest(TEST_SUBTYPE)
|
||||
doGetReplyTest(queryWithSubtype = true)
|
||||
}
|
||||
|
||||
private fun doGetReplyTest(subtype: String?) {
|
||||
private fun doGetReplyTest(queryWithSubtype: Boolean) {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, subtype)
|
||||
val queriedName = if (subtype == null) arrayOf("_testservice", "_tcp", "local")
|
||||
else arrayOf(subtype, "_sub", "_testservice", "_tcp", "local")
|
||||
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
|
||||
setOf(TEST_SUBTYPE, TEST_SUBTYPE2))
|
||||
val queriedName = if (!queryWithSubtype) arrayOf("_testservice", "_tcp", "local")
|
||||
else arrayOf(TEST_SUBTYPE, "_sub", "_testservice", "_tcp", "local")
|
||||
|
||||
val questions = listOf(MdnsPointerRecord(queriedName, false /* isUnicast */))
|
||||
val query = MdnsPacket(0 /* flags */, questions, listOf() /* answers */,
|
||||
|
|
@ -577,8 +592,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0 /* flags */,
|
||||
|
|
@ -605,8 +620,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServicesCaseInsensitive() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
|
||||
val packet = MdnsPacket(
|
||||
0 /* flags */,
|
||||
|
|
@ -633,8 +648,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServices_IdenticalService() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
|
||||
val otherTtlMillis = 1234L
|
||||
val packet = MdnsPacket(
|
||||
|
|
@ -662,8 +677,8 @@ class MdnsRecordRepositoryTest {
|
|||
@Test
|
||||
fun testGetConflictingServicesCaseInsensitive_IdenticalService() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, flags)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
|
||||
|
||||
val otherTtlMillis = 1234L
|
||||
val packet = MdnsPacket(
|
||||
|
|
@ -718,8 +733,7 @@ class MdnsRecordRepositoryTest {
|
|||
MdnsFeatureFlags.newBuilder().setIncludeInetAddressRecordsInProbing(true).build())
|
||||
repository.updateAddresses(TEST_ADDRESSES)
|
||||
assertEquals(0, repository.servicesCount)
|
||||
assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
|
||||
null /* subtype */))
|
||||
assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1))
|
||||
assertEquals(1, repository.servicesCount)
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
|
|
@ -778,10 +792,11 @@ class MdnsRecordRepositoryTest {
|
|||
private fun MdnsRecordRepository.initWithService(
|
||||
serviceId: Int,
|
||||
serviceInfo: NsdServiceInfo,
|
||||
subtype: String? = null,
|
||||
subtypes: Set<String> = setOf(),
|
||||
): AnnouncementInfo {
|
||||
updateAddresses(TEST_ADDRESSES)
|
||||
addService(serviceId, serviceInfo, subtype)
|
||||
serviceInfo.setSubtypes(subtypes)
|
||||
addService(serviceId, serviceInfo)
|
||||
val probingInfo = setServiceProbing(serviceId)
|
||||
assertNotNull(probingInfo)
|
||||
return onProbingSucceeded(probingInfo)
|
||||
|
|
|
|||
Loading…
Reference in a new issue