[mdns] add service-side impl for subtypes advertising
This is the servide-side impl of the subtypes advertising added in
aosp/2608627. See that CL for more backgrounds.
Bug: 265095929
Test: atest CtsNetTestCases FrameworksNetTests
Change-Id: I3d6f96822ac89d12fe29c00f688d848297c25787
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 2640332..9c01dda 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -111,7 +111,9 @@
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 @@
"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 @@
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 @@
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 @@
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 @@
servInfo,
network == null ? INetd.LOCAL_NET_ID : network.netId,
serviceInfo.getInterfaceIndex());
+ servInfo.setSubtypes(dedupSubtypeLabels(serviceInfo.getSubtypes()));
return servInfo;
}
@@ -1591,18 +1636,17 @@
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 @@
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());