• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import static android.Manifest.permission.DEVICE_POWER;
20 import static android.Manifest.permission.NETWORK_SETTINGS;
21 import static android.Manifest.permission.NETWORK_STACK;
22 import static android.net.ConnectivityManager.NETID_UNSET;
23 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
24 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
25 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
26 import static android.net.nsd.AdvertisingRequest.FLAG_SKIP_PROBING;
27 import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
28 import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
29 import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
30 import static android.net.nsd.NsdManager.SUBTYPE_LABEL_REGEX;
31 import static android.net.nsd.NsdManager.TYPE_REGEX;
32 import static android.os.Process.SYSTEM_UID;
33 import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
34 
35 import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
36 import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
37 import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics;
38 import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
39 import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
40 import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE;
41 import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
42 import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
43 
44 import android.annotation.NonNull;
45 import android.annotation.Nullable;
46 import android.annotation.RequiresApi;
47 import android.app.ActivityManager;
48 import android.content.Context;
49 import android.content.Intent;
50 import android.net.ConnectivityManager;
51 import android.net.INetd;
52 import android.net.InetAddresses;
53 import android.net.LinkProperties;
54 import android.net.Network;
55 import android.net.mdns.aidl.DiscoveryInfo;
56 import android.net.mdns.aidl.GetAddressInfo;
57 import android.net.mdns.aidl.IMDnsEventListener;
58 import android.net.mdns.aidl.RegistrationInfo;
59 import android.net.mdns.aidl.ResolutionInfo;
60 import android.net.nsd.AdvertisingRequest;
61 import android.net.nsd.DiscoveryRequest;
62 import android.net.nsd.INsdManager;
63 import android.net.nsd.INsdManagerCallback;
64 import android.net.nsd.INsdServiceConnector;
65 import android.net.nsd.IOffloadEngine;
66 import android.net.nsd.MDnsManager;
67 import android.net.nsd.NsdManager;
68 import android.net.nsd.NsdServiceInfo;
69 import android.net.nsd.OffloadEngine;
70 import android.net.nsd.OffloadServiceInfo;
71 import android.net.wifi.WifiManager;
72 import android.os.Binder;
73 import android.os.Build;
74 import android.os.Handler;
75 import android.os.HandlerThread;
76 import android.os.IBinder;
77 import android.os.Looper;
78 import android.os.Message;
79 import android.os.RemoteCallbackList;
80 import android.os.RemoteException;
81 import android.os.UserHandle;
82 import android.provider.DeviceConfig;
83 import android.text.TextUtils;
84 import android.util.ArraySet;
85 import android.util.Log;
86 import android.util.Pair;
87 import android.util.SparseArray;
88 
89 import com.android.internal.annotations.VisibleForTesting;
90 import com.android.internal.util.IndentingPrintWriter;
91 import com.android.internal.util.State;
92 import com.android.internal.util.StateMachine;
93 import com.android.metrics.NetworkNsdReportedMetrics;
94 import com.android.modules.utils.build.SdkLevel;
95 import com.android.net.module.util.CollectionUtils;
96 import com.android.net.module.util.DeviceConfigUtils;
97 import com.android.net.module.util.DnsUtils;
98 import com.android.net.module.util.HandlerUtils;
99 import com.android.net.module.util.InetAddressUtils;
100 import com.android.net.module.util.PermissionUtils;
101 import com.android.net.module.util.SharedLog;
102 import com.android.server.connectivity.mdns.ExecutorProvider;
103 import com.android.server.connectivity.mdns.MdnsAdvertiser;
104 import com.android.server.connectivity.mdns.MdnsAdvertisingOptions;
105 import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
106 import com.android.server.connectivity.mdns.MdnsFeatureFlags;
107 import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
108 import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
109 import com.android.server.connectivity.mdns.MdnsSearchOptions;
110 import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
111 import com.android.server.connectivity.mdns.MdnsServiceInfo;
112 import com.android.server.connectivity.mdns.MdnsSocketProvider;
113 import com.android.server.connectivity.mdns.util.MdnsUtils;
114 
115 import java.io.FileDescriptor;
116 import java.io.PrintWriter;
117 import java.net.Inet6Address;
118 import java.net.InetAddress;
119 import java.net.NetworkInterface;
120 import java.net.SocketException;
121 import java.net.UnknownHostException;
122 import java.time.Duration;
123 import java.util.ArrayList;
124 import java.util.Arrays;
125 import java.util.Collection;
126 import java.util.Collections;
127 import java.util.HashMap;
128 import java.util.LinkedHashMap;
129 import java.util.List;
130 import java.util.Map;
131 import java.util.Objects;
132 import java.util.Set;
133 import java.util.regex.Matcher;
134 import java.util.regex.Pattern;
135 
136 /**
137  * Network Service Discovery Service handles remote service discovery operation requests by
138  * implementing the INsdManager interface.
139  *
140  * @hide
141  */
142 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
143 public class NsdService extends INsdManager.Stub {
144     private static final String TAG = "NsdService";
145     private static final String MDNS_TAG = "mDnsConnector";
146     /**
147      * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder
148      * implementation.
149      */
150     private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
151     private static final String LOCAL_DOMAIN_NAME = "local";
152 
153     /**
154      * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
155      * implementation.
156      */
157     private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
158 
159     /**
160      * Comma-separated list of type:flag mappings indicating the flags to use to allowlist
161      * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type.
162      *
163      * For example _mytype._tcp.local and _othertype._tcp.local would be configured with:
164      * _mytype._tcp:mytype,_othertype._tcp.local:othertype
165      *
166      * In which case the flags:
167      * "mdns_discovery_manager_allowlist_mytype_version",
168      * "mdns_advertiser_allowlist_mytype_version",
169      * "mdns_discovery_manager_allowlist_othertype_version",
170      * "mdns_advertiser_allowlist_othertype_version"
171      * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
172      * be read with
173      * {@link DeviceConfigUtils#isTetheringFeatureEnabled}
174      *
175      * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
176      * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
177      * @see #MDNS_ALLOWLIST_FLAG_SUFFIX
178      */
179     private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags";
180 
181     private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX =
182             "mdns_discovery_manager_allowlist_";
183     private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX =
184             "mdns_advertiser_allowlist_";
185     private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
186 
187     private static final String FORCE_ENABLE_FLAG_FOR_TEST_PREFIX = "test_";
188 
189     @VisibleForTesting
190     static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
191             "mdns_config_running_app_active_importance_cutoff";
192     @VisibleForTesting
193     static final int DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
194             ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
195     private final int mRunningAppActiveImportanceCutoff;
196 
197     public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
198     private static final long CLEANUP_DELAY_MS = 10000;
199     private static final int IFACE_IDX_ANY = 0;
200     private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100;
201     @VisibleForTesting
202     static final int NO_TRANSACTION = -1;
203     private static final int NO_SENT_QUERY_COUNT = 0;
204     private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
205     private static final int MAX_SUBTYPE_COUNT = 100;
206     private static final int DNSSEC_PROTOCOL = 3;
207     private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
208 
209     private final Context mContext;
210     private final NsdStateMachine mNsdStateMachine;
211     // It can be null on V+ device since mdns native service provided by netd is removed.
212     private final @Nullable MDnsManager mMDnsManager;
213     private final MDnsEventCallback mMDnsEventCallback;
214     @NonNull
215     private final Dependencies mDeps;
216     @NonNull
217     private final MdnsMultinetworkSocketClient mMdnsSocketClient;
218     @NonNull
219     private final MdnsDiscoveryManager mMdnsDiscoveryManager;
220     @NonNull
221     private final MdnsSocketProvider mMdnsSocketProvider;
222     @NonNull
223     private final MdnsAdvertiser mAdvertiser;
224     @NonNull
225     private final Clock mClock;
226     private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
227     // WARNING : Accessing these values in any thread is not safe, it must only be changed in the
228     // state machine thread. If change this outside state machine, it will need to introduce
229     // synchronization.
230     private boolean mIsDaemonStarted = false;
231     private boolean mIsMonitoringSocketsStarted = false;
232 
233     /**
234      * Clients receiving asynchronous messages
235      */
236     private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
237 
238     /* A map from transaction(unique) id to client info */
239     private final SparseArray<ClientInfo> mTransactionIdToClientInfoMap = new SparseArray<>();
240 
241     // Note this is not final to avoid depending on the Wi-Fi service starting before NsdService
242     @Nullable
243     private WifiManager.MulticastLock mHeldMulticastLock;
244     // Fulfilled network requests that require the Wi-Fi lock: key is the obtained Network
245     // (non-null), value is the requested Network (nullable)
246     @NonNull
247     private final ArraySet<Network> mWifiLockRequiredNetworks = new ArraySet<>();
248     @NonNull
249     private final ArraySet<Integer> mRunningAppActiveUids = new ArraySet<>();
250 
251     private final long mCleanupDelayMs;
252 
253     private static final int INVALID_ID = 0;
254     private int mUniqueId = 1;
255     // The count of the connected legacy clients.
256     private int mLegacyClientCount = 0;
257     // The number of client that ever connected.
258     private int mClientNumberId = 1;
259 
260     private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
261             new RemoteCallbackList<>();
262     @NonNull
263     private final MdnsFeatureFlags mMdnsFeatureFlags;
264 
265     private static class OffloadEngineInfo {
266         @NonNull final String mInterfaceName;
267         final long mOffloadCapabilities;
268         final long mOffloadType;
269         @NonNull final IOffloadEngine mOffloadEngine;
270 
OffloadEngineInfo(@onNull IOffloadEngine offloadEngine, @NonNull String interfaceName, long capabilities, long offloadType)271         OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine,
272                 @NonNull String interfaceName, long capabilities, long offloadType) {
273             this.mOffloadEngine = offloadEngine;
274             this.mInterfaceName = interfaceName;
275             this.mOffloadCapabilities = capabilities;
276             this.mOffloadType = offloadType;
277         }
278     }
279 
280     @VisibleForTesting
281     abstract static class MdnsListener implements MdnsServiceBrowserListener {
282         protected final int mClientRequestId;
283         protected final int mTransactionId;
284         @NonNull
285         protected final String mListenedServiceType;
286 
MdnsListener(int clientRequestId, int transactionId, @NonNull String listenedServiceType)287         MdnsListener(int clientRequestId, int transactionId, @NonNull String listenedServiceType) {
288             mClientRequestId = clientRequestId;
289             mTransactionId = transactionId;
290             mListenedServiceType = listenedServiceType;
291         }
292 
293         @NonNull
getListenedServiceType()294         public String getListenedServiceType() {
295             return mListenedServiceType;
296         }
297 
298         @Override
onServiceFound(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)299         public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
300                 boolean isServiceFromCache) { }
301 
302         @Override
onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)303         public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
304 
305         @Override
onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)306         public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
307 
308         @Override
onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)309         public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
310                 boolean isServiceFromCache) { }
311 
312         @Override
onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)313         public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
314 
315         @Override
onSearchStoppedWithError(int error)316         public void onSearchStoppedWithError(int error) { }
317 
318         @Override
onSearchFailedToStart()319         public void onSearchFailedToStart() { }
320 
321         @Override
onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)322         public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
323                 int sentQueryTransactionId) { }
324 
325         @Override
onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode)326         public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
327 
328         // Ensure toString gets overridden
329         @NonNull
toString()330         public abstract String toString();
331     }
332 
333     private class DiscoveryListener extends MdnsListener {
334 
DiscoveryListener(int clientRequestId, int transactionId, @NonNull String listenServiceType)335         DiscoveryListener(int clientRequestId, int transactionId,
336                 @NonNull String listenServiceType) {
337             super(clientRequestId, transactionId, listenServiceType);
338         }
339 
340         @Override
onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)341         public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
342                 boolean isServiceFromCache) {
343             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
344                     NsdManager.SERVICE_FOUND,
345                     new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
346         }
347 
348         @Override
onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)349         public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
350             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
351                     NsdManager.SERVICE_LOST,
352                     new MdnsEvent(mClientRequestId, serviceInfo));
353         }
354 
355         @Override
onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)356         public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
357                 int sentQueryTransactionId) {
358             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
359                     DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
360         }
361 
362         @NonNull
363         @Override
toString()364         public String toString() {
365             return String.format("DiscoveryListener: serviceType=%s", getListenedServiceType());
366         }
367     }
368 
369     private class ResolutionListener extends MdnsListener {
370         private final String mServiceName;
371 
ResolutionListener(int clientRequestId, int transactionId, @NonNull String listenServiceType, @NonNull String serviceName)372         ResolutionListener(int clientRequestId, int transactionId,
373                 @NonNull String listenServiceType, @NonNull String serviceName) {
374             super(clientRequestId, transactionId, listenServiceType);
375             mServiceName = serviceName;
376         }
377 
378         @Override
onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache)379         public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) {
380             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
381                     NsdManager.RESOLVE_SERVICE_SUCCEEDED,
382                     new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
383         }
384 
385         @Override
onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)386         public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
387                 int sentQueryTransactionId) {
388             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
389                     DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
390         }
391 
392         @NonNull
393         @Override
toString()394         public String toString() {
395             return String.format("ResolutionListener serviceName=%s, serviceType=%s",
396                     mServiceName, getListenedServiceType());
397         }
398     }
399 
400     private class ServiceInfoListener extends MdnsListener {
401         private final String mServiceName;
402 
ServiceInfoListener(int clientRequestId, int transactionId, @NonNull String listenServiceType, @NonNull String serviceName)403         ServiceInfoListener(int clientRequestId, int transactionId,
404                 @NonNull String listenServiceType, @NonNull String serviceName) {
405             super(clientRequestId, transactionId, listenServiceType);
406             this.mServiceName = serviceName;
407         }
408 
409         @Override
onServiceFound(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)410         public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
411                 boolean isServiceFromCache) {
412             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
413                     NsdManager.SERVICE_UPDATED,
414                     new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
415         }
416 
417         @Override
onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)418         public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
419             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
420                     NsdManager.SERVICE_UPDATED,
421                     new MdnsEvent(mClientRequestId, serviceInfo));
422         }
423 
424         @Override
onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)425         public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
426             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
427                     NsdManager.SERVICE_UPDATED_LOST,
428                     new MdnsEvent(mClientRequestId, serviceInfo));
429         }
430 
431         @Override
onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)432         public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
433                 int sentQueryTransactionId) {
434             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
435                     DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
436         }
437 
438         @NonNull
439         @Override
toString()440         public String toString() {
441             return String.format("ServiceInfoListener serviceName=%s, serviceType=%s",
442                     mServiceName, getListenedServiceType());
443         }
444     }
445 
446     private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
447         @Override
onSocketRequestFulfilled(@ullable Network socketNetwork, @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports)448         public void onSocketRequestFulfilled(@Nullable Network socketNetwork,
449                 @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) {
450             // The network may be null for Wi-Fi SoftAp interfaces (tethering), but there is no APF
451             // filtering on such interfaces, so taking the multicast lock is not necessary to
452             // disable APF filtering of multicast.
453             if (socketNetwork == null
454                     || !CollectionUtils.contains(transports, TRANSPORT_WIFI)
455                     || CollectionUtils.contains(transports, TRANSPORT_VPN)) {
456                 return;
457             }
458 
459             if (mWifiLockRequiredNetworks.add(socketNetwork)) {
460                 updateMulticastLock();
461             }
462         }
463 
464         @Override
onSocketDestroyed(@ullable Network socketNetwork, @NonNull MdnsInterfaceSocket socket)465         public void onSocketDestroyed(@Nullable Network socketNetwork,
466                 @NonNull MdnsInterfaceSocket socket) {
467             if (mWifiLockRequiredNetworks.remove(socketNetwork)) {
468                 updateMulticastLock();
469             }
470         }
471     }
472 
473     private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
474         private final Handler mHandler;
475 
UidImportanceListener(Handler handler)476         private UidImportanceListener(Handler handler) {
477             mHandler = handler;
478         }
479 
480         @Override
onUidImportance(int uid, int importance)481         public void onUidImportance(int uid, int importance) {
482             mHandler.post(() -> handleUidImportanceChanged(uid, importance));
483         }
484     }
485 
handleUidImportanceChanged(int uid, int importance)486     private void handleUidImportanceChanged(int uid, int importance) {
487         // Lower importance values are more "important"
488         final boolean modified = importance <= mRunningAppActiveImportanceCutoff
489                 ? mRunningAppActiveUids.add(uid)
490                 : mRunningAppActiveUids.remove(uid);
491         if (modified) {
492             updateMulticastLock();
493         }
494     }
495 
496     /**
497      * Take or release the lock based on updated internal state.
498      *
499      * This determines whether the lock needs to be held based on
500      * {@link #mWifiLockRequiredNetworks}, {@link #mRunningAppActiveUids} and
501      * {@link ClientInfo#mClientRequests}, so it must be called after any of the these have been
502      * updated.
503      */
updateMulticastLock()504     private void updateMulticastLock() {
505         final int needsLockUid = getMulticastLockNeededUid();
506         if (needsLockUid >= 0 && mHeldMulticastLock == null) {
507             final WifiManager wm = mContext.getSystemService(WifiManager.class);
508             if (wm == null) {
509                 Log.wtf(TAG, "Got a TRANSPORT_WIFI network without WifiManager");
510                 return;
511             }
512             mHeldMulticastLock = wm.createMulticastLock(TAG);
513             mHeldMulticastLock.acquire();
514             mServiceLogs.log("Taking multicast lock for uid " + needsLockUid);
515         } else if (needsLockUid < 0 && mHeldMulticastLock != null) {
516             mHeldMulticastLock.release();
517             mHeldMulticastLock = null;
518             mServiceLogs.log("Released multicast lock");
519         }
520     }
521 
522     /**
523      * @return The UID of an app requiring the multicast lock, or -1 if none.
524      */
getMulticastLockNeededUid()525     private int getMulticastLockNeededUid() {
526         if (mWifiLockRequiredNetworks.size() == 0) {
527             // Return early if NSD is not active, or not on any relevant network
528             return -1;
529         }
530         for (int i = 0; i < mTransactionIdToClientInfoMap.size(); i++) {
531             final ClientInfo clientInfo = mTransactionIdToClientInfoMap.valueAt(i);
532             if (!mRunningAppActiveUids.contains(clientInfo.mUid)) {
533                 // Ignore non-active UIDs
534                 continue;
535             }
536 
537             if (clientInfo.hasAnyJavaBackendRequestForNetworks(mWifiLockRequiredNetworks)) {
538                 return clientInfo.mUid;
539             }
540         }
541         return -1;
542     }
543 
544     /**
545      * Data class of mdns service callback information.
546      */
547     private static class MdnsEvent {
548         final int mClientRequestId;
549         @Nullable
550         final MdnsServiceInfo mMdnsServiceInfo;
551         final boolean mIsServiceFromCache;
552 
MdnsEvent(int clientRequestId)553         MdnsEvent(int clientRequestId) {
554             this(clientRequestId, null /* mdnsServiceInfo */, false /* isServiceFromCache */);
555         }
556 
MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo)557         MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo) {
558             this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
559         }
560 
MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo, boolean isServiceFromCache)561         MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo,
562                 boolean isServiceFromCache) {
563             mClientRequestId = clientRequestId;
564             mMdnsServiceInfo = mdnsServiceInfo;
565             mIsServiceFromCache = isServiceFromCache;
566         }
567     }
568 
569     // TODO: Use a Handler instead of a StateMachine since there are no state changes.
570     private class NsdStateMachine extends StateMachine {
571 
572         private final EnabledState mEnabledState = new EnabledState();
573 
574         @Override
getWhatToString(int what)575         protected String getWhatToString(int what) {
576             return NsdManager.nameOf(what);
577         }
578 
maybeStartDaemon()579         private void maybeStartDaemon() {
580             if (mIsDaemonStarted) {
581                 if (DBG) Log.d(TAG, "Daemon is already started.");
582                 return;
583             }
584 
585             if (mMDnsManager == null) {
586                 Log.wtf(TAG, "maybeStartDaemon: mMDnsManager is null");
587                 return;
588             }
589             mMDnsManager.registerEventListener(mMDnsEventCallback);
590             mMDnsManager.startDaemon();
591             mIsDaemonStarted = true;
592             maybeScheduleStop();
593             mServiceLogs.log("Start mdns_responder daemon");
594         }
595 
maybeStopDaemon()596         private void maybeStopDaemon() {
597             if (!mIsDaemonStarted) {
598                 if (DBG) Log.d(TAG, "Daemon has not been started.");
599                 return;
600             }
601 
602             if (mMDnsManager == null) {
603                 Log.wtf(TAG, "maybeStopDaemon: mMDnsManager is null");
604                 return;
605             }
606             mMDnsManager.unregisterEventListener(mMDnsEventCallback);
607             mMDnsManager.stopDaemon();
608             mIsDaemonStarted = false;
609             mServiceLogs.log("Stop mdns_responder daemon");
610         }
611 
isAnyRequestActive()612         private boolean isAnyRequestActive() {
613             return mTransactionIdToClientInfoMap.size() != 0;
614         }
615 
scheduleStop()616         private void scheduleStop() {
617             sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
618         }
maybeScheduleStop()619         private void maybeScheduleStop() {
620             // The native daemon should stay alive and can't be cleanup
621             // if any legacy client connected.
622             if (!isAnyRequestActive() && mLegacyClientCount == 0) {
623                 scheduleStop();
624             }
625         }
626 
cancelStop()627         private void cancelStop() {
628             this.removeMessages(NsdManager.DAEMON_CLEANUP);
629         }
630 
maybeStartMonitoringSockets()631         private void maybeStartMonitoringSockets() {
632             if (mIsMonitoringSocketsStarted) {
633                 if (DBG) Log.d(TAG, "Socket monitoring is already started.");
634                 return;
635             }
636 
637             mMdnsSocketProvider.startMonitoringSockets();
638             mIsMonitoringSocketsStarted = true;
639         }
640 
maybeStopMonitoringSocketsIfNoActiveRequest()641         private void maybeStopMonitoringSocketsIfNoActiveRequest() {
642             if (!mIsMonitoringSocketsStarted) return;
643             if (isAnyRequestActive()) return;
644 
645             mMdnsSocketProvider.requestStopWhenInactive();
646             mIsMonitoringSocketsStarted = false;
647         }
648 
NsdStateMachine(String name, Handler handler)649         NsdStateMachine(String name, Handler handler) {
650             super(name, handler);
651             addState(mEnabledState);
652             State initialState = mEnabledState;
653             setInitialState(initialState);
654             setLogRecSize(25);
655         }
656 
657         class EnabledState extends State {
658             @Override
enter()659             public void enter() {
660                 sendNsdStateChangeBroadcast(true);
661             }
662 
663             @Override
exit()664             public void exit() {
665                 // TODO: it is incorrect to stop the daemon without expunging all requests
666                 // and sending error callbacks to clients.
667                 scheduleStop();
668             }
669 
requestLimitReached(ClientInfo clientInfo)670             private boolean requestLimitReached(ClientInfo clientInfo) {
671                 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) {
672                     if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
673                     return true;
674                 }
675                 return false;
676             }
677 
storeLegacyRequestMap(int clientRequestId, int transactionId, ClientInfo clientInfo, int what, long startTimeMs)678             private ClientRequest storeLegacyRequestMap(int clientRequestId, int transactionId,
679                     ClientInfo clientInfo, int what, long startTimeMs) {
680                 final LegacyClientRequest request =
681                         new LegacyClientRequest(transactionId, what, startTimeMs);
682                 clientInfo.mClientRequests.put(clientRequestId, request);
683                 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
684                 // Remove the cleanup event because here comes a new request.
685                 cancelStop();
686                 return request;
687             }
688 
storeAdvertiserRequestMap(int clientRequestId, int transactionId, ClientInfo clientInfo, @NonNull NsdServiceInfo serviceInfo)689             private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
690                     ClientInfo clientInfo, @NonNull NsdServiceInfo serviceInfo) {
691                 final String serviceFullName =
692                         serviceInfo.getServiceName() + "." + serviceInfo.getServiceType();
693                 clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
694                         transactionId, serviceInfo.getNetwork(), serviceFullName,
695                         mClock.elapsedRealtime()));
696                 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
697                 updateMulticastLock();
698             }
699 
removeRequestMap( int clientRequestId, int transactionId, ClientInfo clientInfo)700             private void removeRequestMap(
701                     int clientRequestId, int transactionId, ClientInfo clientInfo) {
702                 final ClientRequest existing = clientInfo.mClientRequests.get(clientRequestId);
703                 if (existing == null) return;
704                 clientInfo.mClientRequests.remove(clientRequestId);
705                 mTransactionIdToClientInfoMap.remove(transactionId);
706 
707                 if (existing instanceof LegacyClientRequest) {
708                     maybeScheduleStop();
709                 } else {
710                     maybeStopMonitoringSocketsIfNoActiveRequest();
711                     updateMulticastLock();
712                 }
713             }
714 
storeDiscoveryManagerRequestMap(int clientRequestId, int transactionId, MdnsListener listener, ClientInfo clientInfo, @Nullable Network requestedNetwork)715             private ClientRequest storeDiscoveryManagerRequestMap(int clientRequestId,
716                     int transactionId, MdnsListener listener, ClientInfo clientInfo,
717                     @Nullable Network requestedNetwork) {
718                 final DiscoveryManagerRequest request = new DiscoveryManagerRequest(transactionId,
719                         listener, requestedNetwork, mClock.elapsedRealtime());
720                 clientInfo.mClientRequests.put(clientRequestId, request);
721                 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
722                 updateMulticastLock();
723                 return request;
724             }
725 
726             /**
727              * Truncate a service name to up to 63 UTF-8 bytes.
728              *
729              * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating
730              * names used in registerService follows historical behavior (see mdnsresponder
731              * handle_regservice_request).
732              */
733             @NonNull
truncateServiceName(@onNull String originalName)734             private String truncateServiceName(@NonNull String originalName) {
735                 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH);
736             }
737 
stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId, int transactionId, ClientInfo clientInfo)738             private void stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId,
739                     int transactionId, ClientInfo clientInfo) {
740                 clientInfo.unregisterMdnsListenerFromRequest(request);
741                 removeRequestMap(clientRequestId, transactionId, clientInfo);
742             }
743 
getClientInfoForReply(Message msg)744             private ClientInfo getClientInfoForReply(Message msg) {
745                 final ListenerArgs args = (ListenerArgs) msg.obj;
746                 return mClients.get(args.connector);
747             }
748 
749             /**
750              * Returns {@code false} if {@code subtypes} exceeds the maximum number limit or
751              * contains invalid subtype label.
752              */
checkSubtypeLabels(Set<String> subtypes)753             private boolean checkSubtypeLabels(Set<String> subtypes) {
754                 if (subtypes.size() > MAX_SUBTYPE_COUNT) {
755                     mServiceLogs.e(
756                             "Too many subtypes: " + subtypes.size() + " (max = "
757                                     + MAX_SUBTYPE_COUNT + ")");
758                     return false;
759                 }
760 
761                 for (String subtype : subtypes) {
762                     if (!checkSubtypeLabel(subtype)) {
763                         mServiceLogs.e("Subtype " + subtype + " is invalid");
764                         return false;
765                     }
766                 }
767                 return true;
768             }
769 
dedupSubtypeLabels(Collection<String> subtypes)770             private Set<String> dedupSubtypeLabels(Collection<String> subtypes) {
771                 final Map<String, String> subtypeMap = new LinkedHashMap<>(subtypes.size());
772                 for (String subtype : subtypes) {
773                     subtypeMap.put(DnsUtils.toDnsUpperCase(subtype), subtype);
774                 }
775                 return new ArraySet<>(subtypeMap.values());
776             }
777 
checkTtl( @ullable Duration ttl, @NonNull ClientInfo clientInfo)778             private boolean checkTtl(
779                         @Nullable Duration ttl, @NonNull ClientInfo clientInfo) {
780                 if (ttl == null) {
781                     return true;
782                 }
783 
784                 final long ttlSeconds = ttl.toSeconds();
785                 final int uid = clientInfo.getUid();
786 
787                 // Allows Thread module in the system_server to register TTL that is smaller than
788                 // 30 seconds
789                 final long minTtlSeconds = uid == SYSTEM_UID ? 0 : NsdManager.TTL_SECONDS_MIN;
790 
791                 // Allows Thread module in the system_server to register TTL that is larger than
792                 // 10 hours
793                 final long maxTtlSeconds =
794                         uid == SYSTEM_UID ? 0xffffffffL : NsdManager.TTL_SECONDS_MAX;
795 
796                 if (ttlSeconds < minTtlSeconds || ttlSeconds > maxTtlSeconds) {
797                     mServiceLogs.e("ttlSeconds exceeds allowed range (value = "
798                             + ttlSeconds + ", allowedRange = [" + minTtlSeconds
799                             + ", " + maxTtlSeconds + " ])");
800                     return false;
801                 }
802                 return true;
803             }
804 
805             @Override
processMessage(Message msg)806             public boolean processMessage(Message msg) {
807                 final ClientInfo clientInfo;
808                 final int transactionId;
809                 final int clientRequestId = msg.arg2;
810                 final OffloadEngineInfo offloadEngineInfo;
811                 switch (msg.what) {
812                     case NsdManager.DISCOVER_SERVICES: {
813                         if (DBG) Log.d(TAG, "Discover services");
814                         final DiscoveryArgs discoveryArgs = (DiscoveryArgs) msg.obj;
815                         clientInfo = mClients.get(discoveryArgs.connector);
816                         // If the binder death notification for a INsdManagerCallback was received
817                         // before any calls are received by NsdService, the clientInfo would be
818                         // cleared and cause NPE. Add a null check here to prevent this corner case.
819                         if (clientInfo == null) {
820                             Log.e(TAG, "Unknown connector in discovery");
821                             break;
822                         }
823 
824                         if (requestLimitReached(clientInfo)) {
825                             clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
826                                     NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
827                             break;
828                         }
829 
830                         final DiscoveryRequest discoveryRequest = discoveryArgs.discoveryRequest;
831                         transactionId = getUniqueId();
832                         final Pair<String, List<String>> typeAndSubtype =
833                                 parseTypeAndSubtype(discoveryRequest.getServiceType());
834                         final String serviceType = typeAndSubtype == null
835                                 ? null : typeAndSubtype.first;
836                         if (clientInfo.mUseJavaBackend
837                                 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
838                                 || useDiscoveryManagerForType(serviceType)) {
839                             if (serviceType == null || typeAndSubtype.second.size() > 1) {
840                                 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
841                                         NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
842                                 break;
843                             }
844 
845                             String subtype = discoveryRequest.getSubtype();
846                             if (subtype == null && !typeAndSubtype.second.isEmpty()) {
847                                 subtype = typeAndSubtype.second.get(0);
848                             }
849 
850                             if (subtype != null && !checkSubtypeLabel(subtype)) {
851                                 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
852                                         NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
853                                 break;
854                             }
855 
856                             final String listenServiceType = serviceType + ".local";
857                             maybeStartMonitoringSockets();
858                             final MdnsListener listener = new DiscoveryListener(clientRequestId,
859                                     transactionId, listenServiceType);
860                             final MdnsSearchOptions.Builder optionsBuilder =
861                                     MdnsSearchOptions.newBuilder()
862                                             .setNetwork(discoveryRequest.getNetwork())
863                                             .setRemoveExpiredService(true)
864                                             .setQueryMode(
865                                                     mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
866                                                             ? AGGRESSIVE_QUERY_MODE
867                                                             : PASSIVE_QUERY_MODE);
868                             if (subtype != null) {
869                                 // checkSubtypeLabels() ensures that subtypes start with '_' but
870                                 // MdnsSearchOptions expects the underscore to not be present.
871                                 optionsBuilder.addSubtype(subtype.substring(1));
872                             }
873                             mMdnsDiscoveryManager.registerListener(
874                                     listenServiceType, listener, optionsBuilder.build());
875                             final ClientRequest request = storeDiscoveryManagerRequestMap(
876                                     clientRequestId, transactionId, listener, clientInfo,
877                                     discoveryRequest.getNetwork());
878                             clientInfo.onDiscoverServicesStarted(
879                                     clientRequestId, discoveryRequest, request);
880                             clientInfo.log("Register a DiscoveryListener " + transactionId
881                                     + " for service type:" + listenServiceType);
882                         } else {
883                             maybeStartDaemon();
884                             if (discoverServices(transactionId, discoveryRequest)) {
885                                 if (DBG) {
886                                     Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId
887                                             + discoveryRequest.getServiceType());
888                                 }
889                                 final ClientRequest request = storeLegacyRequestMap(clientRequestId,
890                                         transactionId, clientInfo, msg.what,
891                                         mClock.elapsedRealtime());
892                                 clientInfo.onDiscoverServicesStarted(
893                                         clientRequestId, discoveryRequest, request);
894                             } else {
895                                 stopServiceDiscovery(transactionId);
896                                 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
897                                         NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
898                             }
899                         }
900                         break;
901                     }
902                     case NsdManager.STOP_DISCOVERY: {
903                         if (DBG) Log.d(TAG, "Stop service discovery");
904                         final ListenerArgs args = (ListenerArgs) msg.obj;
905                         clientInfo = mClients.get(args.connector);
906                         // If the binder death notification for a INsdManagerCallback was received
907                         // before any calls are received by NsdService, the clientInfo would be
908                         // cleared and cause NPE. Add a null check here to prevent this corner case.
909                         if (clientInfo == null) {
910                             Log.e(TAG, "Unknown connector in stop discovery");
911                             break;
912                         }
913 
914                         final ClientRequest request =
915                                 clientInfo.mClientRequests.get(clientRequestId);
916                         if (request == null) {
917                             Log.e(TAG, "Unknown client request in STOP_DISCOVERY");
918                             break;
919                         }
920                         transactionId = request.mTransactionId;
921                         // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
922                         // point, so this needs to check the type of the original request to
923                         // unregister instead of looking at the flag value.
924                         if (request instanceof DiscoveryManagerRequest) {
925                             stopDiscoveryManagerRequest(
926                                     request, clientRequestId, transactionId, clientInfo);
927                             clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
928                             clientInfo.log("Unregister the DiscoveryListener " + transactionId);
929                         } else {
930                             removeRequestMap(clientRequestId, transactionId, clientInfo);
931                             if (stopServiceDiscovery(transactionId)) {
932                                 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
933                             } else {
934                                 clientInfo.onStopDiscoveryFailed(
935                                         clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
936                             }
937                         }
938                         break;
939                     }
940                     case NsdManager.REGISTER_SERVICE: {
941                         if (DBG) Log.d(TAG, "Register service");
942                         final AdvertisingArgs args = (AdvertisingArgs) msg.obj;
943                         clientInfo = mClients.get(args.connector);
944                         // If the binder death notification for a INsdManagerCallback was received
945                         // before any calls are received by NsdService, the clientInfo would be
946                         // cleared and cause NPE. Add a null check here to prevent this corner case.
947                         if (clientInfo == null) {
948                             Log.e(TAG, "Unknown connector in registration");
949                             break;
950                         }
951 
952                         if (requestLimitReached(clientInfo)) {
953                             clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
954                                     NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
955                             break;
956                         }
957                         final AdvertisingRequest advertisingRequest = args.advertisingRequest;
958                         if (advertisingRequest == null) {
959                             Log.e(TAG, "Unknown advertisingRequest in registration");
960                             break;
961                         }
962                         final NsdServiceInfo serviceInfo = advertisingRequest.getServiceInfo();
963                         final String serviceType = serviceInfo.getServiceType();
964                         final Pair<String, List<String>> typeSubtype = parseTypeAndSubtype(
965                                 serviceType);
966                         final String registerServiceType = typeSubtype == null
967                                 ? null : typeSubtype.first;
968                         final String hostname = serviceInfo.getHostname();
969                         // Keep compatible with the legacy behavior: It's allowed to set host
970                         // addresses for a service registration although the host addresses
971                         // won't be registered. To register the addresses for a host, the
972                         // hostname must be specified.
973                         if (hostname == null) {
974                             serviceInfo.setHostAddresses(Collections.emptyList());
975                         }
976                         if (clientInfo.mUseJavaBackend
977                                 || mDeps.isMdnsAdvertiserEnabled(mContext)
978                                 || useAdvertiserForType(registerServiceType)) {
979                             if (serviceType != null && registerServiceType == null) {
980                                 Log.e(TAG, "Invalid service type: " + serviceType);
981                                 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
982                                         NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
983                                 break;
984                             }
985                             boolean isUpdateOnly = (advertisingRequest.getFlags()
986                                     & AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY) > 0;
987                             // If it is an update request, then reuse the old transactionId
988                             if (isUpdateOnly) {
989                                 final ClientRequest existingClientRequest =
990                                         clientInfo.mClientRequests.get(clientRequestId);
991                                 if (existingClientRequest == null) {
992                                     Log.e(TAG, "Invalid update on requestId: " + clientRequestId);
993                                     clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
994                                             NsdManager.FAILURE_INTERNAL_ERROR,
995                                             false /* isLegacy */);
996                                     break;
997                                 }
998                                 transactionId = existingClientRequest.mTransactionId;
999                             } else {
1000                                 transactionId = getUniqueId();
1001                             }
1002 
1003                             if (registerServiceType != null) {
1004                                 serviceInfo.setServiceType(registerServiceType);
1005                                 serviceInfo.setServiceName(
1006                                         truncateServiceName(serviceInfo.getServiceName()));
1007                             }
1008 
1009                             if (!checkHostname(hostname)) {
1010                                 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
1011                                         NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
1012                                 break;
1013                             }
1014 
1015                             if (!checkPublicKey(serviceInfo.getPublicKey())) {
1016                                 Log.e(TAG,
1017                                         "Invalid public key: "
1018                                                 + Arrays.toString(serviceInfo.getPublicKey()));
1019                                 clientInfo.onRegisterServiceFailedImmediately(
1020                                         clientRequestId,
1021                                         NsdManager.FAILURE_BAD_PARAMETERS,
1022                                         false /* isLegacy */);
1023                                 break;
1024                             }
1025 
1026                             Set<String> subtypes = new ArraySet<>(serviceInfo.getSubtypes());
1027                             if (typeSubtype != null && typeSubtype.second != null) {
1028                                 for (String subType : typeSubtype.second) {
1029                                     if (!TextUtils.isEmpty(subType)) {
1030                                         subtypes.add(subType);
1031                                     }
1032                                 }
1033                             }
1034                             subtypes = dedupSubtypeLabels(subtypes);
1035 
1036                             if (!checkSubtypeLabels(subtypes)) {
1037                                 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
1038                                         NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
1039                                 break;
1040                             }
1041 
1042                             if (!checkTtl(advertisingRequest.getTtl(), clientInfo)) {
1043                                 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
1044                                         NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
1045                                 break;
1046                             }
1047 
1048                             serviceInfo.setSubtypes(subtypes);
1049                             maybeStartMonitoringSockets();
1050                             final boolean skipProbing = (advertisingRequest.getFlags()
1051                                     & FLAG_SKIP_PROBING) > 0;
1052                             final MdnsAdvertisingOptions mdnsAdvertisingOptions =
1053                                     MdnsAdvertisingOptions.newBuilder()
1054                                             .setIsOnlyUpdate(isUpdateOnly)
1055                                             .setSkipProbing(skipProbing)
1056                                             .setTtl(advertisingRequest.getTtl())
1057                                             .build();
1058                             mAdvertiser.addOrUpdateService(transactionId, serviceInfo,
1059                                     mdnsAdvertisingOptions, clientInfo.mUid);
1060                             storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
1061                                     serviceInfo);
1062                         } else {
1063                             maybeStartDaemon();
1064                             transactionId = getUniqueId();
1065                             if (registerService(transactionId, serviceInfo)) {
1066                                 if (DBG) {
1067                                     Log.d(TAG, "Register " + clientRequestId
1068                                             + " " + transactionId);
1069                                 }
1070                                 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
1071                                         msg.what, mClock.elapsedRealtime());
1072                                 // Return success after mDns reports success
1073                             } else {
1074                                 unregisterService(transactionId);
1075                                 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
1076                                         NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
1077                             }
1078 
1079                         }
1080                         break;
1081                     }
1082                     case NsdManager.UNREGISTER_SERVICE: {
1083                         if (DBG) Log.d(TAG, "unregister service");
1084                         final ListenerArgs args = (ListenerArgs) msg.obj;
1085                         clientInfo = mClients.get(args.connector);
1086                         // If the binder death notification for a INsdManagerCallback was received
1087                         // before any calls are received by NsdService, the clientInfo would be
1088                         // cleared and cause NPE. Add a null check here to prevent this corner case.
1089                         if (clientInfo == null) {
1090                             Log.e(TAG, "Unknown connector in unregistration");
1091                             break;
1092                         }
1093                         final ClientRequest request =
1094                                 clientInfo.mClientRequests.get(clientRequestId);
1095                         if (request == null) {
1096                             Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE");
1097                             break;
1098                         }
1099                         transactionId = request.mTransactionId;
1100                         removeRequestMap(clientRequestId, transactionId, clientInfo);
1101 
1102                         // Note isMdnsAdvertiserEnabled may have changed to false at this point,
1103                         // so this needs to check the type of the original request to unregister
1104                         // instead of looking at the flag value.
1105                         if (request instanceof AdvertiserClientRequest) {
1106                             final AdvertiserMetrics metrics =
1107                                     mAdvertiser.getAdvertiserMetrics(transactionId);
1108                             mAdvertiser.removeService(transactionId);
1109                             clientInfo.onUnregisterServiceSucceeded(
1110                                     clientRequestId, request, metrics);
1111                         } else {
1112                             if (unregisterService(transactionId)) {
1113                                 clientInfo.onUnregisterServiceSucceeded(clientRequestId, request,
1114                                         new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */,
1115                                                 NO_PACKET /* sentPacketCount */,
1116                                                 0 /* conflictDuringProbingCount */,
1117                                                 0 /* conflictAfterProbingCount */));
1118                             } else {
1119                                 clientInfo.onUnregisterServiceFailed(
1120                                         clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
1121                             }
1122                         }
1123                         break;
1124                     }
1125                     case NsdManager.RESOLVE_SERVICE: {
1126                         if (DBG) Log.d(TAG, "Resolve service");
1127                         final ListenerArgs args = (ListenerArgs) msg.obj;
1128                         clientInfo = mClients.get(args.connector);
1129                         // If the binder death notification for a INsdManagerCallback was received
1130                         // before any calls are received by NsdService, the clientInfo would be
1131                         // cleared and cause NPE. Add a null check here to prevent this corner case.
1132                         if (clientInfo == null) {
1133                             Log.e(TAG, "Unknown connector in resolution");
1134                             break;
1135                         }
1136 
1137                         final NsdServiceInfo info = args.serviceInfo;
1138                         transactionId = getUniqueId();
1139                         final Pair<String, List<String>> typeSubtype =
1140                                 parseTypeAndSubtype(info.getServiceType());
1141                         final String serviceType = typeSubtype == null
1142                                 ? null : typeSubtype.first;
1143                         if (clientInfo.mUseJavaBackend
1144                                 ||  mDeps.isMdnsDiscoveryManagerEnabled(mContext)
1145                                 || useDiscoveryManagerForType(serviceType)) {
1146                             if (serviceType == null) {
1147                                 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1148                                         NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
1149                                 break;
1150                             }
1151                             final String resolveServiceType = serviceType + ".local";
1152 
1153                             maybeStartMonitoringSockets();
1154                             final MdnsListener listener = new ResolutionListener(clientRequestId,
1155                                     transactionId, resolveServiceType, info.getServiceName());
1156                             final int ifaceIdx = info.getNetwork() != null
1157                                     ? 0 : info.getInterfaceIndex();
1158                             final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1159                                     .setNetwork(info.getNetwork())
1160                                     .setInterfaceIndex(ifaceIdx)
1161                                     .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
1162                                             ? AGGRESSIVE_QUERY_MODE
1163                                             : PASSIVE_QUERY_MODE)
1164                                     .setResolveInstanceName(info.getServiceName())
1165                                     .setRemoveExpiredService(true)
1166                                     .build();
1167                             mMdnsDiscoveryManager.registerListener(
1168                                     resolveServiceType, listener, options);
1169                             storeDiscoveryManagerRequestMap(clientRequestId, transactionId,
1170                                     listener, clientInfo, info.getNetwork());
1171                             clientInfo.log("Register a ResolutionListener " + transactionId
1172                                     + " for service type:" + resolveServiceType);
1173                         } else {
1174                             if (clientInfo.mResolvedService != null) {
1175                                 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1176                                         NsdManager.FAILURE_ALREADY_ACTIVE, true /* isLegacy */);
1177                                 break;
1178                             }
1179 
1180                             maybeStartDaemon();
1181                             if (resolveService(transactionId, info)) {
1182                                 clientInfo.mResolvedService = new NsdServiceInfo();
1183                                 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
1184                                         msg.what, mClock.elapsedRealtime());
1185                             } else {
1186                                 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1187                                         NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
1188                             }
1189                         }
1190                         break;
1191                     }
1192                     case NsdManager.STOP_RESOLUTION: {
1193                         if (DBG) Log.d(TAG, "Stop service resolution");
1194                         final ListenerArgs args = (ListenerArgs) msg.obj;
1195                         clientInfo = mClients.get(args.connector);
1196                         // If the binder death notification for a INsdManagerCallback was received
1197                         // before any calls are received by NsdService, the clientInfo would be
1198                         // cleared and cause NPE. Add a null check here to prevent this corner case.
1199                         if (clientInfo == null) {
1200                             Log.e(TAG, "Unknown connector in stop resolution");
1201                             break;
1202                         }
1203 
1204                         final ClientRequest request =
1205                                 clientInfo.mClientRequests.get(clientRequestId);
1206                         if (request == null) {
1207                             Log.e(TAG, "Unknown client request in STOP_RESOLUTION");
1208                             break;
1209                         }
1210                         transactionId = request.mTransactionId;
1211                         // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
1212                         // point, so this needs to check the type of the original request to
1213                         // unregister instead of looking at the flag value.
1214                         if (request instanceof DiscoveryManagerRequest) {
1215                             stopDiscoveryManagerRequest(
1216                                     request, clientRequestId, transactionId, clientInfo);
1217                             clientInfo.onStopResolutionSucceeded(clientRequestId, request);
1218                             clientInfo.log("Unregister the ResolutionListener " + transactionId);
1219                         } else {
1220                             removeRequestMap(clientRequestId, transactionId, clientInfo);
1221                             if (stopResolveService(transactionId)) {
1222                                 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
1223                             } else {
1224                                 clientInfo.onStopResolutionFailed(
1225                                         clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
1226                             }
1227                             clientInfo.mResolvedService = null;
1228                         }
1229                         break;
1230                     }
1231                     case NsdManager.REGISTER_SERVICE_CALLBACK: {
1232                         if (DBG) Log.d(TAG, "Register a service callback");
1233                         final ListenerArgs args = (ListenerArgs) msg.obj;
1234                         clientInfo = mClients.get(args.connector);
1235                         // If the binder death notification for a INsdManagerCallback was received
1236                         // before any calls are received by NsdService, the clientInfo would be
1237                         // cleared and cause NPE. Add a null check here to prevent this corner case.
1238                         if (clientInfo == null) {
1239                             Log.e(TAG, "Unknown connector in callback registration");
1240                             break;
1241                         }
1242 
1243                         final NsdServiceInfo info = args.serviceInfo;
1244                         transactionId = getUniqueId();
1245                         final Pair<String, List<String>> typeAndSubtype =
1246                                 parseTypeAndSubtype(info.getServiceType());
1247                         final String serviceType = typeAndSubtype == null
1248                                 ? null : typeAndSubtype.first;
1249                         if (serviceType == null) {
1250                             clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId,
1251                                     NsdManager.FAILURE_BAD_PARAMETERS);
1252                             break;
1253                         }
1254                         final String resolveServiceType = serviceType + ".local";
1255 
1256                         maybeStartMonitoringSockets();
1257                         final MdnsListener listener = new ServiceInfoListener(clientRequestId,
1258                                 transactionId, resolveServiceType, info.getServiceName());
1259                         final int ifIndex = info.getNetwork() != null
1260                                 ? 0 : info.getInterfaceIndex();
1261                         final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1262                                 .setNetwork(info.getNetwork())
1263                                 .setInterfaceIndex(ifIndex)
1264                                 .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
1265                                         ? AGGRESSIVE_QUERY_MODE
1266                                         : PASSIVE_QUERY_MODE)
1267                                 .setResolveInstanceName(info.getServiceName())
1268                                 .setRemoveExpiredService(true)
1269                                 .build();
1270                         mMdnsDiscoveryManager.registerListener(
1271                                 resolveServiceType, listener, options);
1272                         storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener,
1273                                 clientInfo, info.getNetwork());
1274                         clientInfo.onServiceInfoCallbackRegistered(transactionId);
1275                         clientInfo.log("Register a ServiceInfoListener " + transactionId
1276                                 + " for service type:" + resolveServiceType);
1277                         break;
1278                     }
1279                     case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
1280                         if (DBG) Log.d(TAG, "Unregister a service callback");
1281                         final ListenerArgs args = (ListenerArgs) msg.obj;
1282                         clientInfo = mClients.get(args.connector);
1283                         // If the binder death notification for a INsdManagerCallback was received
1284                         // before any calls are received by NsdService, the clientInfo would be
1285                         // cleared and cause NPE. Add a null check here to prevent this corner case.
1286                         if (clientInfo == null) {
1287                             Log.e(TAG, "Unknown connector in callback unregistration");
1288                             break;
1289                         }
1290 
1291                         final ClientRequest request =
1292                                 clientInfo.mClientRequests.get(clientRequestId);
1293                         if (request == null) {
1294                             Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK");
1295                             break;
1296                         }
1297                         transactionId = request.mTransactionId;
1298                         if (request instanceof DiscoveryManagerRequest) {
1299                             stopDiscoveryManagerRequest(
1300                                     request, clientRequestId, transactionId, clientInfo);
1301                             clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request);
1302                             clientInfo.log("Unregister the ServiceInfoListener " + transactionId);
1303                         } else {
1304                             loge("Unregister failed with non-DiscoveryManagerRequest.");
1305                         }
1306                         break;
1307                     }
1308                     case MDNS_SERVICE_EVENT:
1309                         if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
1310                             return NOT_HANDLED;
1311                         }
1312                         break;
1313                     case MDNS_DISCOVERY_MANAGER_EVENT:
1314                         if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) {
1315                             return NOT_HANDLED;
1316                         }
1317                         break;
1318                     case NsdManager.REGISTER_OFFLOAD_ENGINE:
1319                         offloadEngineInfo = (OffloadEngineInfo) msg.obj;
1320                         // TODO: Limits the number of registrations created by a given class.
1321                         mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
1322                                 offloadEngineInfo);
1323                         sendAllOffloadServiceInfos(offloadEngineInfo);
1324                         break;
1325                     case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
1326                         mOffloadEngines.unregister((IOffloadEngine) msg.obj);
1327                         break;
1328                     case NsdManager.REGISTER_CLIENT:
1329                         final ConnectorArgs arg = (ConnectorArgs) msg.obj;
1330                         final INsdManagerCallback cb = arg.callback;
1331                         try {
1332                             cb.asBinder().linkToDeath(arg.connector, 0);
1333                             final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
1334                             final NetworkNsdReportedMetrics metrics =
1335                                     mDeps.makeNetworkNsdReportedMetrics(
1336                                             (int) mClock.elapsedRealtime());
1337                             clientInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend,
1338                                     mServiceLogs.forSubComponent(tag), metrics);
1339                             mClients.put(arg.connector, clientInfo);
1340                         } catch (RemoteException e) {
1341                             Log.w(TAG, "Client request id " + clientRequestId
1342                                     + " has already died");
1343                         }
1344                         break;
1345                     case NsdManager.UNREGISTER_CLIENT:
1346                         final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
1347                         clientInfo = mClients.remove(connector);
1348                         if (clientInfo != null) {
1349                             clientInfo.expungeAllRequests();
1350                             if (clientInfo.isPreSClient()) {
1351                                 mLegacyClientCount -= 1;
1352                             }
1353                         }
1354                         maybeStopMonitoringSocketsIfNoActiveRequest();
1355                         maybeScheduleStop();
1356                         break;
1357                     case NsdManager.DAEMON_CLEANUP:
1358                         maybeStopDaemon();
1359                         break;
1360                     // This event should be only sent by the legacy (target SDK < S) clients.
1361                     // Mark the sending client as legacy.
1362                     case NsdManager.DAEMON_STARTUP:
1363                         clientInfo = getClientInfoForReply(msg);
1364                         if (clientInfo != null) {
1365                             cancelStop();
1366                             clientInfo.setPreSClient();
1367                             mLegacyClientCount += 1;
1368                             maybeStartDaemon();
1369                         }
1370                         break;
1371                     default:
1372                         Log.wtf(TAG, "Unhandled " + msg);
1373                         return NOT_HANDLED;
1374                 }
1375                 return HANDLED;
1376             }
1377 
handleMDnsServiceEvent(int code, int transactionId, Object obj)1378             private boolean handleMDnsServiceEvent(int code, int transactionId, Object obj) {
1379                 NsdServiceInfo servInfo;
1380                 ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
1381                 if (clientInfo == null) {
1382                     Log.e(TAG, String.format(
1383                             "transactionId %d for %d has no client mapping", transactionId, code));
1384                     return false;
1385                 }
1386 
1387                 /* This goes in response as msg.arg2 */
1388                 int clientRequestId = clientInfo.getClientRequestId(transactionId);
1389                 if (clientRequestId < 0) {
1390                     // This can happen because of race conditions. For example,
1391                     // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
1392                     // and we may get in this situation.
1393                     Log.d(TAG, String.format("%d for transactionId %d that is no longer active",
1394                             code, transactionId));
1395                     return false;
1396                 }
1397                 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1398                 if (request == null) {
1399                     Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1400                     return false;
1401                 }
1402                 if (DBG) {
1403                     Log.d(TAG, String.format(
1404                             "MDns service event code:%d transactionId=%d", code, transactionId));
1405                 }
1406                 switch (code) {
1407                     case IMDnsEventListener.SERVICE_FOUND: {
1408                         final DiscoveryInfo info = (DiscoveryInfo) obj;
1409                         final String name = info.serviceName;
1410                         final String type = info.registrationType;
1411                         servInfo = new NsdServiceInfo(name, type);
1412                         final int foundNetId = info.netId;
1413                         if (foundNetId == 0L) {
1414                             // Ignore services that do not have a Network: they are not usable
1415                             // by apps, as they would need privileged permissions to use
1416                             // interfaces that do not have an associated Network.
1417                             break;
1418                         }
1419                         if (foundNetId == INetd.DUMMY_NET_ID) {
1420                             // Ignore services on the dummy0 interface: they are only seen when
1421                             // discovering locally advertised services, and are not reachable
1422                             // through that interface.
1423                             break;
1424                         }
1425                         setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
1426 
1427                         clientInfo.onServiceFound(clientRequestId, servInfo, request);
1428                         break;
1429                     }
1430                     case IMDnsEventListener.SERVICE_LOST: {
1431                         final DiscoveryInfo info = (DiscoveryInfo) obj;
1432                         final String name = info.serviceName;
1433                         final String type = info.registrationType;
1434                         final int lostNetId = info.netId;
1435                         servInfo = new NsdServiceInfo(name, type);
1436                         // The network could be set to null (netId 0) if it was torn down when the
1437                         // service is lost
1438                         // TODO: avoid returning null in that case, possibly by remembering
1439                         // found services on the same interface index and their network at the time
1440                         setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
1441                         clientInfo.onServiceLost(clientRequestId, servInfo, request);
1442                         break;
1443                     }
1444                     case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
1445                         clientInfo.onDiscoverServicesFailed(clientRequestId,
1446                                 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1447                                 transactionId,
1448                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
1449                         break;
1450                     case IMDnsEventListener.SERVICE_REGISTERED: {
1451                         final RegistrationInfo info = (RegistrationInfo) obj;
1452                         final String name = info.serviceName;
1453                         servInfo = new NsdServiceInfo(name, null /* serviceType */);
1454                         clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, request);
1455                         break;
1456                     }
1457                     case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
1458                         clientInfo.onRegisterServiceFailed(clientRequestId,
1459                                 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1460                                 transactionId,
1461                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
1462                         break;
1463                     case IMDnsEventListener.SERVICE_RESOLVED: {
1464                         final ResolutionInfo info = (ResolutionInfo) obj;
1465                         int index = 0;
1466                         final String fullName = info.serviceFullName;
1467                         while (index < fullName.length() && fullName.charAt(index) != '.') {
1468                             if (fullName.charAt(index) == '\\') {
1469                                 ++index;
1470                             }
1471                             ++index;
1472                         }
1473                         if (index >= fullName.length()) {
1474                             Log.e(TAG, "Invalid service found " + fullName);
1475                             break;
1476                         }
1477 
1478                         String name = unescape(fullName.substring(0, index));
1479                         String rest = fullName.substring(index);
1480                         String type = rest.replace(".local.", "");
1481 
1482                         final NsdServiceInfo serviceInfo = clientInfo.mResolvedService;
1483                         serviceInfo.setServiceName(name);
1484                         serviceInfo.setServiceType(type);
1485                         serviceInfo.setPort(info.port);
1486                         serviceInfo.setTxtRecords(info.txtRecord);
1487                         // Network will be added after SERVICE_GET_ADDR_SUCCESS
1488 
1489                         stopResolveService(transactionId);
1490                         removeRequestMap(clientRequestId, transactionId, clientInfo);
1491 
1492                         final int transactionId2 = getUniqueId();
1493                         if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) {
1494                             storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo,
1495                                     NsdManager.RESOLVE_SERVICE, request.mStartTimeMs);
1496                         } else {
1497                             clientInfo.onResolveServiceFailed(clientRequestId,
1498                                     NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1499                                     transactionId,
1500                                     request.calculateRequestDurationMs(mClock.elapsedRealtime()));
1501                             clientInfo.mResolvedService = null;
1502                         }
1503                         break;
1504                     }
1505                     case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
1506                         /* NNN resolveId errorCode */
1507                         stopResolveService(transactionId);
1508                         removeRequestMap(clientRequestId, transactionId, clientInfo);
1509                         clientInfo.onResolveServiceFailed(clientRequestId,
1510                                 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1511                                 transactionId,
1512                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
1513                         clientInfo.mResolvedService = null;
1514                         break;
1515                     case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
1516                         /* NNN resolveId errorCode */
1517                         stopGetAddrInfo(transactionId);
1518                         removeRequestMap(clientRequestId, transactionId, clientInfo);
1519                         clientInfo.onResolveServiceFailed(clientRequestId,
1520                                 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1521                                 transactionId,
1522                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
1523                         clientInfo.mResolvedService = null;
1524                         break;
1525                     case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
1526                         /* NNN resolveId hostname ttl addr interfaceIdx netId */
1527                         final GetAddressInfo info = (GetAddressInfo) obj;
1528                         final String address = info.address;
1529                         final int netId = info.netId;
1530                         InetAddress serviceHost = null;
1531                         try {
1532                             serviceHost = InetAddress.getByName(address);
1533                         } catch (UnknownHostException e) {
1534                             Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
1535                         }
1536 
1537                         // If the resolved service is on an interface without a network, consider it
1538                         // as a failure: it would not be usable by apps as they would need
1539                         // privileged permissions.
1540                         if (netId != NETID_UNSET && serviceHost != null) {
1541                             clientInfo.mResolvedService.setHost(serviceHost);
1542                             setServiceNetworkForCallback(clientInfo.mResolvedService,
1543                                     netId, info.interfaceIdx);
1544                             clientInfo.onResolveServiceSucceeded(
1545                                     clientRequestId, clientInfo.mResolvedService, request);
1546                         } else {
1547                             clientInfo.onResolveServiceFailed(clientRequestId,
1548                                     NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1549                                     transactionId,
1550                                     request.calculateRequestDurationMs(mClock.elapsedRealtime()));
1551                         }
1552                         stopGetAddrInfo(transactionId);
1553                         removeRequestMap(clientRequestId, transactionId, clientInfo);
1554                         clientInfo.mResolvedService = null;
1555                         break;
1556                     }
1557                     default:
1558                         return false;
1559                 }
1560                 return true;
1561             }
1562 
1563             @Nullable
buildNsdServiceInfoFromMdnsEvent( final MdnsEvent event, int code)1564             private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(
1565                     final MdnsEvent event, int code) {
1566                 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1567                 final String[] typeArray = serviceInfo.getServiceType();
1568                 final String joinedType;
1569                 if (typeArray.length == 0
1570                         || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) {
1571                     Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: "
1572                             + Arrays.toString(typeArray));
1573                     return null;
1574                 } else {
1575                     joinedType = TextUtils.join(".",
1576                             Arrays.copyOfRange(typeArray, 0, typeArray.length - 1));
1577                 }
1578                 final String serviceType;
1579                 switch (code) {
1580                     case NsdManager.SERVICE_FOUND:
1581                     case NsdManager.SERVICE_LOST:
1582                         // For consistency with historical behavior, discovered service types have
1583                         // a dot at the end.
1584                         serviceType = joinedType + ".";
1585                         break;
1586                     case RESOLVE_SERVICE_SUCCEEDED:
1587                         // For consistency with historical behavior, resolved service types have
1588                         // a dot at the beginning.
1589                         serviceType = "." + joinedType;
1590                         break;
1591                     default:
1592                         serviceType = joinedType;
1593                         break;
1594                 }
1595                 final String serviceName = serviceInfo.getServiceInstanceName();
1596                 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
1597                 final Network network = serviceInfo.getNetwork();
1598                 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a
1599                 // network for Tethering interface. In other words, the network == null means the
1600                 // network has netId = INetd.LOCAL_NET_ID.
1601                 setServiceNetworkForCallback(
1602                         servInfo,
1603                         network == null ? INetd.LOCAL_NET_ID : network.netId,
1604                         serviceInfo.getInterfaceIndex());
1605                 servInfo.setSubtypes(dedupSubtypeLabels(serviceInfo.getSubtypes()));
1606                 servInfo.setExpirationTime(serviceInfo.getExpirationTime());
1607                 return servInfo;
1608             }
1609 
handleMdnsDiscoveryManagerEvent( int transactionId, int code, Object obj)1610             private boolean handleMdnsDiscoveryManagerEvent(
1611                     int transactionId, int code, Object obj) {
1612                 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
1613                 if (clientInfo == null) {
1614                     Log.e(TAG, String.format(
1615                             "id %d for %d has no client mapping", transactionId, code));
1616                     return false;
1617                 }
1618 
1619                 final MdnsEvent event = (MdnsEvent) obj;
1620                 final int clientRequestId = event.mClientRequestId;
1621                 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1622                 if (request == null) {
1623                     Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1624                     return false;
1625                 }
1626 
1627                 // Deal with the discovery sent callback
1628                 if (code == DISCOVERY_QUERY_SENT_CALLBACK) {
1629                     request.onQuerySent();
1630                     return true;
1631                 }
1632 
1633                 // Deal with other callbacks.
1634                 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
1635                 // Errors are already logged if null
1636                 if (info == null) return false;
1637                 mServiceLogs.log(String.format(
1638                         "MdnsDiscoveryManager event code=%s transactionId=%d",
1639                         NsdManager.nameOf(code), transactionId));
1640                 switch (code) {
1641                     case NsdManager.SERVICE_FOUND:
1642                         // Set the ServiceFromCache flag only if the service is actually being
1643                         // retrieved from the cache. This flag should not be overridden by later
1644                         // service found event, which may not be cached.
1645                         if (event.mIsServiceFromCache) {
1646                             request.setServiceFromCache(true);
1647                         }
1648                         clientInfo.onServiceFound(clientRequestId, info, request);
1649                         break;
1650                     case NsdManager.SERVICE_LOST:
1651                         clientInfo.onServiceLost(clientRequestId, info, request);
1652                         break;
1653                     case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
1654                         final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1655                         info.setPort(serviceInfo.getPort());
1656 
1657                         Map<String, String> attrs = serviceInfo.getAttributes();
1658                         for (Map.Entry<String, String> kv : attrs.entrySet()) {
1659                             final String key = kv.getKey();
1660                             try {
1661                                 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1662                             } catch (IllegalArgumentException e) {
1663                                 Log.e(TAG, "Invalid attribute", e);
1664                             }
1665                         }
1666                         info.setHostname(getHostname(serviceInfo));
1667                         final List<InetAddress> addresses = getInetAddresses(serviceInfo);
1668                         if (addresses.size() != 0) {
1669                             info.setHostAddresses(addresses);
1670                             request.setServiceFromCache(event.mIsServiceFromCache);
1671                             clientInfo.onResolveServiceSucceeded(clientRequestId, info, request);
1672                         } else {
1673                             // No address. Notify resolution failure.
1674                             clientInfo.onResolveServiceFailed(clientRequestId,
1675                                     NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */,
1676                                     transactionId,
1677                                     request.calculateRequestDurationMs(mClock.elapsedRealtime()));
1678                         }
1679 
1680                         // Unregister the listener immediately like IMDnsEventListener design
1681                         if (!(request instanceof DiscoveryManagerRequest)) {
1682                             Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event");
1683                             break;
1684                         }
1685                         stopDiscoveryManagerRequest(
1686                                 request, clientRequestId, transactionId, clientInfo);
1687                         break;
1688                     }
1689                     case NsdManager.SERVICE_UPDATED: {
1690                         final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1691                         info.setPort(serviceInfo.getPort());
1692 
1693                         Map<String, String> attrs = serviceInfo.getAttributes();
1694                         for (Map.Entry<String, String> kv : attrs.entrySet()) {
1695                             final String key = kv.getKey();
1696                             try {
1697                                 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1698                             } catch (IllegalArgumentException e) {
1699                                 Log.e(TAG, "Invalid attribute", e);
1700                             }
1701                         }
1702 
1703                         info.setHostname(getHostname(serviceInfo));
1704                         final List<InetAddress> addresses = getInetAddresses(serviceInfo);
1705                         info.setHostAddresses(addresses);
1706                         clientInfo.onServiceUpdated(clientRequestId, info, request);
1707                         // Set the ServiceFromCache flag only if the service is actually being
1708                         // retrieved from the cache. This flag should not be overridden by later
1709                         // service updates, which may not be cached.
1710                         if (event.mIsServiceFromCache) {
1711                             request.setServiceFromCache(true);
1712                         }
1713                         break;
1714                     }
1715                     case NsdManager.SERVICE_UPDATED_LOST:
1716                         clientInfo.onServiceUpdatedLost(clientRequestId, request);
1717                         break;
1718                     default:
1719                         return false;
1720                 }
1721                 return true;
1722             }
1723        }
1724     }
1725 
1726     @NonNull
getInetAddresses(@onNull MdnsServiceInfo serviceInfo)1727     private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) {
1728         final List<String> v4Addrs = serviceInfo.getIpv4Addresses();
1729         final List<String> v6Addrs = serviceInfo.getIpv6Addresses();
1730         final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size());
1731         for (String ipv4Address : v4Addrs) {
1732             try {
1733                 addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
1734             } catch (IllegalArgumentException e) {
1735                 Log.wtf(TAG, "Invalid ipv4 address", e);
1736             }
1737         }
1738         for (String ipv6Address : v6Addrs) {
1739             try {
1740                 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress(
1741                         ipv6Address);
1742                 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex()));
1743             } catch (IllegalArgumentException e) {
1744                 Log.wtf(TAG, "Invalid ipv6 address", e);
1745             }
1746         }
1747         return addresses;
1748     }
1749 
1750     @NonNull
getHostname(@onNull MdnsServiceInfo serviceInfo)1751     private static String getHostname(@NonNull MdnsServiceInfo serviceInfo) {
1752         String[] hostname = serviceInfo.getHostName();
1753         // Strip the "local" top-level domain.
1754         if (hostname.length >= 2 && hostname[hostname.length - 1].equals("local")) {
1755             hostname = Arrays.copyOf(hostname, hostname.length - 1);
1756         }
1757         return String.join(".", hostname);
1758     }
1759 
setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx)1760     private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
1761         switch (netId) {
1762             case NETID_UNSET:
1763                 info.setNetwork(null);
1764                 break;
1765             case INetd.LOCAL_NET_ID:
1766                 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
1767                 // visible / usable for apps, so do not return it. Store the interface
1768                 // index instead, so at least if the client tries to resolve the service
1769                 // with that NsdServiceInfo, it will be done on the same interface.
1770                 // If they recreate the NsdServiceInfo themselves, resolution would be
1771                 // done on all interfaces as before T, which should also work.
1772                 info.setNetwork(null);
1773                 info.setInterfaceIndex(ifaceIdx);
1774                 break;
1775             default:
1776                 info.setNetwork(new Network(netId));
1777         }
1778     }
1779 
1780     // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
1781     // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
1782     // unescape for getting right service address. See "Notes on DNS Name Escaping" on
1783     // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
unescape(String s)1784     private String unescape(String s) {
1785         StringBuilder sb = new StringBuilder(s.length());
1786         for (int i = 0; i < s.length(); ++i) {
1787             char c = s.charAt(i);
1788             if (c == '\\') {
1789                 if (++i >= s.length()) {
1790                     Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1791                     break;
1792                 }
1793                 c = s.charAt(i);
1794                 if (c != '.' && c != '\\') {
1795                     if (i + 2 >= s.length()) {
1796                         Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1797                         break;
1798                     }
1799                     c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
1800                             + (s.charAt(i + 2) - '0'));
1801                     i += 2;
1802                 }
1803             }
1804             sb.append(c);
1805         }
1806         return sb.toString();
1807     }
1808 
1809     /**
1810      * Check the given service type is valid and construct it to a service type
1811      * which can use for discovery / resolution service.
1812      *
1813      * <p>The valid service type should be 2 labels, or 3 labels if the query is for a
1814      * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
1815      * underscore; they are alphanumerical characters or dashes or underscore, except the
1816      * last one that is just alphanumerical. The last label must be _tcp or _udp.
1817      *
1818      * <p>The subtypes may also be specified with a comma after the service type, for example
1819      * _type._tcp,_subtype1,_subtype2
1820      *
1821      * @param serviceType the request service type for discovery / resolution service
1822      * @return constructed service type or null if the given service type is invalid.
1823      */
1824     @Nullable
parseTypeAndSubtype(String serviceType)1825     public static Pair<String, List<String>> parseTypeAndSubtype(String serviceType) {
1826         if (TextUtils.isEmpty(serviceType)) return null;
1827         final Pattern serviceTypePattern = Pattern.compile(TYPE_REGEX);
1828         final Matcher matcher = serviceTypePattern.matcher(serviceType);
1829         if (!matcher.matches()) return null;
1830         final String queryType = matcher.group(2);
1831         // Use the subtype at the beginning
1832         if (matcher.group(1) != null) {
1833             return new Pair<>(queryType, List.of(matcher.group(1)));
1834         }
1835         // Use the subtypes at the end
1836         final String subTypesStr = matcher.group(3);
1837         if (subTypesStr != null && !subTypesStr.isEmpty()) {
1838             final String[] subTypes = subTypesStr.substring(1).split(",");
1839             return new Pair<>(queryType, List.of(subTypes));
1840         }
1841 
1842         return new Pair<>(queryType, Collections.emptyList());
1843     }
1844 
1845     /**
1846      * Checks if the hostname is valid.
1847      *
1848      * <p>For now NsdService only allows single-label hostnames conforming to RFC 1035. In other
1849      * words, the hostname should be at most 63 characters long and it only contains letters, digits
1850      * and hyphens.
1851      *
1852      * <p>Additionally, this allows hostname starting with a digit to support Matter devices. Per
1853      * Matter spec 4.3.1.1:
1854      *
1855      * <p>The target host name SHALL be constructed using one of the available link-layer addresses,
1856      * such as a 48-bit device MAC address (for Ethernet and Wi‑Fi) or a 64-bit MAC Extended Address
1857      * (for Thread) expressed as a fixed-length twelve-character (or sixteen-character) hexadecimal
1858      * string, encoded as ASCII (UTF-8) text using capital letters, e.g., B75AFB458ECD.<domain>.
1859      */
checkHostname(@ullable String hostname)1860     public static boolean checkHostname(@Nullable String hostname) {
1861         if (hostname == null) {
1862             return true;
1863         }
1864         String HOSTNAME_REGEX = "^[a-zA-Z0-9]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$";
1865         return Pattern.compile(HOSTNAME_REGEX).matcher(hostname).matches();
1866     }
1867 
1868     /**
1869      * Checks if the public key is valid.
1870      *
1871      * <p>For simplicity, it only checks if the protocol is DNSSEC and the RDATA is not fewer than 4
1872      * bytes. See RFC 3445 Section 3.
1873      *
1874      * <p>Message format: flags (2 bytes), protocol (1 byte), algorithm (1 byte), public key.
1875      */
checkPublicKey(@ullable byte[] publicKey)1876     private static boolean checkPublicKey(@Nullable byte[] publicKey) {
1877         if (publicKey == null) {
1878             return true;
1879         }
1880         if (publicKey.length < 4) {
1881             return false;
1882         }
1883         int protocol = publicKey[2];
1884         return protocol == DNSSEC_PROTOCOL;
1885     }
1886 
1887     /** Returns {@code true} if {@code subtype} is a valid DNS-SD subtype label. */
checkSubtypeLabel(String subtype)1888     private static boolean checkSubtypeLabel(String subtype) {
1889         return Pattern.compile("^" + SUBTYPE_LABEL_REGEX + "$").matcher(subtype).matches();
1890     }
1891 
1892     @VisibleForTesting
NsdService(Context ctx, Handler handler, long cleanupDelayMs)1893     NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
1894         this(ctx, handler, cleanupDelayMs, new Dependencies());
1895     }
1896 
1897     @VisibleForTesting
NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps)1898     NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) {
1899         mCleanupDelayMs = cleanupDelayMs;
1900         mContext = ctx;
1901         mNsdStateMachine = new NsdStateMachine(TAG, handler);
1902         mNsdStateMachine.start();
1903         // It can fail on V+ device since mdns native service provided by netd is removed.
1904         mMDnsManager = SdkLevel.isAtLeastV() ? null : ctx.getSystemService(MDnsManager.class);
1905         mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
1906         mDeps = deps;
1907 
1908         mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
1909                 LOGGER.forSubComponent("MdnsSocketProvider"), new SocketRequestMonitor());
1910         // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
1911         // address events are received. When the netlink monitor starts, any IP addresses already
1912         // on the interfaces will not be seen. In practice, the network will not connect at boot
1913         // time As a result, all the netlink message should be observed if the netlink monitor
1914         // starts here.
1915         handler.post(mMdnsSocketProvider::startNetLinkMonitor);
1916 
1917         // NsdService is started after ActivityManager (startOtherServices in SystemServer, vs.
1918         // startBootstrapServices).
1919         mRunningAppActiveImportanceCutoff = mDeps.getDeviceConfigInt(
1920                 MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF,
1921                 DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF);
1922         final ActivityManager am = ctx.getSystemService(ActivityManager.class);
1923         am.addOnUidImportanceListener(new UidImportanceListener(handler),
1924                 mRunningAppActiveImportanceCutoff);
1925 
1926         mMdnsFeatureFlags = new MdnsFeatureFlags.Builder()
1927                 .setIsMdnsOffloadFeatureEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1928                         mContext, MdnsFeatureFlags.NSD_FORCE_DISABLE_MDNS_OFFLOAD))
1929                 .setIncludeInetAddressRecordsInProbing(mDeps.isFeatureEnabled(
1930                         mContext, MdnsFeatureFlags.INCLUDE_INET_ADDRESS_RECORDS_IN_PROBING))
1931                 .setIsExpiredServicesRemovalEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1932                         mContext, MdnsFeatureFlags.NSD_EXPIRED_SERVICES_REMOVAL))
1933                 .setIsLabelCountLimitEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1934                         mContext, MdnsFeatureFlags.NSD_LIMIT_LABEL_COUNT))
1935                 .setIsKnownAnswerSuppressionEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1936                         mContext, MdnsFeatureFlags.NSD_KNOWN_ANSWER_SUPPRESSION))
1937                 .setIsUnicastReplyEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1938                         mContext, MdnsFeatureFlags.NSD_UNICAST_REPLY_ENABLED))
1939                 .setIsAggressiveQueryModeEnabled(mDeps.isFeatureEnabled(
1940                         mContext, MdnsFeatureFlags.NSD_AGGRESSIVE_QUERY_MODE))
1941                 .setIsQueryWithKnownAnswerEnabled(mDeps.isFeatureEnabled(
1942                         mContext, MdnsFeatureFlags.NSD_QUERY_WITH_KNOWN_ANSWER))
1943                 .setAvoidAdvertisingEmptyTxtRecords(mDeps.isTetheringFeatureNotChickenedOut(
1944                         mContext, MdnsFeatureFlags.NSD_AVOID_ADVERTISING_EMPTY_TXT_RECORDS))
1945                 .setIsCachedServicesRemovalEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1946                         mContext, MdnsFeatureFlags.NSD_CACHED_SERVICES_REMOVAL))
1947                 .setCachedServicesRetentionTime(mDeps.getDeviceConfigPropertyInt(
1948                         MdnsFeatureFlags.NSD_CACHED_SERVICES_RETENTION_TIME,
1949                         MdnsFeatureFlags.DEFAULT_CACHED_SERVICES_RETENTION_TIME_MILLISECONDS))
1950                 .setIsShortHostnamesEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1951                         mContext, MdnsFeatureFlags.NSD_USE_SHORT_HOSTNAMES))
1952                 .setOverrideProvider(new MdnsFeatureFlags.FlagOverrideProvider() {
1953                     @Override
1954                     public boolean isForceEnabledForTest(@NonNull String flag) {
1955                         return mDeps.isFeatureEnabled(
1956                                 mContext,
1957                                 FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag);
1958                     }
1959 
1960                     @Override
1961                     public int getIntValueForTest(@NonNull String flag, int defaultValue) {
1962                         return mDeps.getDeviceConfigPropertyInt(
1963                                 FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag, defaultValue);
1964                     }
1965                 })
1966                 .build();
1967         mMdnsSocketClient =
1968                 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
1969                         LOGGER.forSubComponent("MdnsMultinetworkSocketClient"), mMdnsFeatureFlags);
1970         mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
1971                 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"),
1972                 mMdnsFeatureFlags);
1973         handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
1974         mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
1975                 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"),
1976                 mMdnsFeatureFlags, mContext);
1977         mClock = deps.makeClock();
1978     }
1979 
1980     /**
1981      * Dependencies of NsdService, for injection in tests.
1982      */
1983     @VisibleForTesting
1984     public static class Dependencies {
1985         /**
1986          * Check whether the MdnsDiscoveryManager feature is enabled.
1987          *
1988          * @param context The global context information about an app environment.
1989          * @return true if the MdnsDiscoveryManager feature is enabled.
1990          */
isMdnsDiscoveryManagerEnabled(Context context)1991         public boolean isMdnsDiscoveryManagerEnabled(Context context) {
1992             return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
1993                     MDNS_DISCOVERY_MANAGER_VERSION);
1994         }
1995 
1996         /**
1997          * Check whether the MdnsAdvertiser feature is enabled.
1998          *
1999          * @param context The global context information about an app environment.
2000          * @return true if the MdnsAdvertiser feature is enabled.
2001          */
isMdnsAdvertiserEnabled(Context context)2002         public boolean isMdnsAdvertiserEnabled(Context context) {
2003             return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
2004                     MDNS_ADVERTISER_VERSION);
2005         }
2006 
2007         /**
2008          * Get the type allowlist flag value.
2009          * @see #MDNS_TYPE_ALLOWLIST_FLAGS
2010          */
2011         @Nullable
getTypeAllowlistFlags()2012         public String getTypeAllowlistFlags() {
2013             return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING,
2014                     MDNS_TYPE_ALLOWLIST_FLAGS, null);
2015         }
2016 
2017         /**
2018          * @see DeviceConfigUtils#isTetheringFeatureEnabled
2019          */
isFeatureEnabled(Context context, String feature)2020         public boolean isFeatureEnabled(Context context, String feature) {
2021             return DeviceConfigUtils.isTetheringFeatureEnabled(context, feature);
2022         }
2023 
2024         /**
2025          * @see DeviceConfigUtils#isTetheringFeatureNotChickenedOut
2026          */
isTetheringFeatureNotChickenedOut(Context context, String feature)2027         public boolean isTetheringFeatureNotChickenedOut(Context context, String feature) {
2028             return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, feature);
2029         }
2030 
2031         /**
2032          * @see DeviceConfigUtils#getDeviceConfigPropertyInt
2033          */
getDeviceConfigPropertyInt(String feature, int defaultValue)2034         public int getDeviceConfigPropertyInt(String feature, int defaultValue) {
2035             return DeviceConfigUtils.getDeviceConfigPropertyInt(
2036                     NAMESPACE_TETHERING, feature, defaultValue);
2037         }
2038 
2039         /**
2040          * @see MdnsDiscoveryManager
2041          */
makeMdnsDiscoveryManager( @onNull ExecutorProvider executorProvider, @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog, @NonNull MdnsFeatureFlags featureFlags)2042         public MdnsDiscoveryManager makeMdnsDiscoveryManager(
2043                 @NonNull ExecutorProvider executorProvider,
2044                 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog,
2045                 @NonNull MdnsFeatureFlags featureFlags) {
2046             return new MdnsDiscoveryManager(
2047                     executorProvider, socketClient, sharedLog, featureFlags);
2048         }
2049 
2050         /**
2051          * @see MdnsAdvertiser
2052          */
makeMdnsAdvertiser( @onNull Looper looper, @NonNull MdnsSocketProvider socketProvider, @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog, MdnsFeatureFlags featureFlags, Context context)2053         public MdnsAdvertiser makeMdnsAdvertiser(
2054                 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
2055                 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog,
2056                 MdnsFeatureFlags featureFlags, Context context) {
2057             return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog, featureFlags, context);
2058         }
2059 
2060         /**
2061          * @see MdnsSocketProvider
2062          */
makeMdnsSocketProvider(@onNull Context context, @NonNull Looper looper, @NonNull SharedLog sharedLog, @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback)2063         public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
2064                 @NonNull Looper looper, @NonNull SharedLog sharedLog,
2065                 @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback) {
2066             return new MdnsSocketProvider(context, looper, sharedLog, socketCreationCallback);
2067         }
2068 
2069         /**
2070          * @see DeviceConfig#getInt(String, String, int)
2071          */
getDeviceConfigInt(@onNull String config, int defaultValue)2072         public int getDeviceConfigInt(@NonNull String config, int defaultValue) {
2073             return DeviceConfig.getInt(NAMESPACE_TETHERING, config, defaultValue);
2074         }
2075 
2076         /**
2077          * @see Binder#getCallingUid()
2078          */
getCallingUid()2079         public int getCallingUid() {
2080             return Binder.getCallingUid();
2081         }
2082 
2083         /**
2084          * @see NetworkNsdReportedMetrics
2085          */
makeNetworkNsdReportedMetrics(int clientId)2086         public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics(int clientId) {
2087             return new NetworkNsdReportedMetrics(clientId);
2088         }
2089 
2090         /**
2091          * @see MdnsUtils.Clock
2092          */
makeClock()2093         public Clock makeClock() {
2094             return new Clock();
2095         }
2096     }
2097 
2098     /**
2099      * Return whether a type is allowlisted to use the Java backend.
2100      * @param type The service type
2101      * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or
2102      *                   {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}.
2103      */
isTypeAllowlistedForJavaBackend(@ullable String type, @NonNull String flagPrefix)2104     private boolean isTypeAllowlistedForJavaBackend(@Nullable String type,
2105             @NonNull String flagPrefix) {
2106         if (type == null) return false;
2107         final String typesConfig = mDeps.getTypeAllowlistFlags();
2108         if (TextUtils.isEmpty(typesConfig)) return false;
2109 
2110         final String mappingPrefix = type + ":";
2111         String mappedFlag = null;
2112         for (String mapping : TextUtils.split(typesConfig, ",")) {
2113             if (mapping.startsWith(mappingPrefix)) {
2114                 mappedFlag = mapping.substring(mappingPrefix.length());
2115                 break;
2116             }
2117         }
2118 
2119         if (mappedFlag == null) return false;
2120 
2121         return mDeps.isFeatureEnabled(mContext,
2122                 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX);
2123     }
2124 
useDiscoveryManagerForType(@ullable String type)2125     private boolean useDiscoveryManagerForType(@Nullable String type) {
2126         return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX);
2127     }
2128 
useAdvertiserForType(@ullable String type)2129     private boolean useAdvertiserForType(@Nullable String type) {
2130         return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX);
2131     }
2132 
create(Context context)2133     public static NsdService create(Context context) {
2134         HandlerThread thread = new HandlerThread(TAG);
2135         thread.start();
2136         Handler handler = new Handler(thread.getLooper());
2137         NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
2138         return service;
2139     }
2140 
2141     private static class MDnsEventCallback extends IMDnsEventListener.Stub {
2142         private final StateMachine mStateMachine;
2143 
MDnsEventCallback(StateMachine sm)2144         MDnsEventCallback(StateMachine sm) {
2145             mStateMachine = sm;
2146         }
2147 
2148         @Override
onServiceRegistrationStatus(final RegistrationInfo status)2149         public void onServiceRegistrationStatus(final RegistrationInfo status) {
2150             mStateMachine.sendMessage(
2151                     MDNS_SERVICE_EVENT, status.result, status.id, status);
2152         }
2153 
2154         @Override
onServiceDiscoveryStatus(final DiscoveryInfo status)2155         public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
2156             mStateMachine.sendMessage(
2157                     MDNS_SERVICE_EVENT, status.result, status.id, status);
2158         }
2159 
2160         @Override
onServiceResolutionStatus(final ResolutionInfo status)2161         public void onServiceResolutionStatus(final ResolutionInfo status) {
2162             mStateMachine.sendMessage(
2163                     MDNS_SERVICE_EVENT, status.result, status.id, status);
2164         }
2165 
2166         @Override
onGettingServiceAddressStatus(final GetAddressInfo status)2167         public void onGettingServiceAddressStatus(final GetAddressInfo status) {
2168             mStateMachine.sendMessage(
2169                     MDNS_SERVICE_EVENT, status.result, status.id, status);
2170         }
2171 
2172         @Override
getInterfaceVersion()2173         public int getInterfaceVersion() throws RemoteException {
2174             return this.VERSION;
2175         }
2176 
2177         @Override
getInterfaceHash()2178         public String getInterfaceHash() throws RemoteException {
2179             return this.HASH;
2180         }
2181     }
2182 
sendAllOffloadServiceInfos(@onNull OffloadEngineInfo offloadEngineInfo)2183     private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) {
2184         final String targetInterface = offloadEngineInfo.mInterfaceName;
2185         final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine;
2186         final List<MdnsAdvertiser.OffloadServiceInfoWrapper> offloadWrappers =
2187                 mAdvertiser.getAllInterfaceOffloadServiceInfos(targetInterface);
2188         for (MdnsAdvertiser.OffloadServiceInfoWrapper wrapper : offloadWrappers) {
2189             try {
2190                 offloadEngine.onOffloadServiceUpdated(wrapper.mOffloadServiceInfo);
2191             } catch (RemoteException e) {
2192                 // Can happen in regular cases, do not log a stacktrace
2193                 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
2194             }
2195         }
2196     }
2197 
sendOffloadServiceInfosUpdate(@onNull String targetInterfaceName, @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove)2198     private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
2199             @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
2200         final int count = mOffloadEngines.beginBroadcast();
2201         try {
2202             for (int i = 0; i < count; i++) {
2203                 final OffloadEngineInfo offloadEngineInfo =
2204                         (OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i);
2205                 final String interfaceName = offloadEngineInfo.mInterfaceName;
2206                 if (!targetInterfaceName.equals(interfaceName)
2207                         || ((offloadEngineInfo.mOffloadType
2208                         & offloadServiceInfo.getOffloadType()) == 0)) {
2209                     continue;
2210                 }
2211                 try {
2212                     if (isRemove) {
2213                         mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved(
2214                                 offloadServiceInfo);
2215                     } else {
2216                         mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated(
2217                                 offloadServiceInfo);
2218                     }
2219                 } catch (RemoteException e) {
2220                     // Can happen in regular cases, do not log a stacktrace
2221                     Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
2222                 }
2223             }
2224         } finally {
2225             mOffloadEngines.finishBroadcast();
2226         }
2227     }
2228 
2229     private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
2230         // TODO: add a callback to notify when a service is being added on each interface (as soon
2231         // as probing starts), and call mOffloadCallbacks. This callback is for
2232         // OFFLOAD_CAPABILITY_FILTER_REPLIES offload type.
2233 
2234         @Override
onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo)2235         public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
2236             mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
2237             final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
2238             if (clientInfo == null) return;
2239 
2240             final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
2241             if (clientRequestId < 0) return;
2242 
2243             // onRegisterServiceSucceeded only has the service name and hostname in its info. This
2244             // aligns with historical behavior.
2245             final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
2246             cbInfo.setHostname(registeredInfo.getHostname());
2247             final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
2248             clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo, request);
2249         }
2250 
2251         @Override
onRegisterServiceFailed(int transactionId, int errorCode)2252         public void onRegisterServiceFailed(int transactionId, int errorCode) {
2253             final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
2254             if (clientInfo == null) return;
2255 
2256             final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
2257             if (clientRequestId < 0) return;
2258             final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
2259             clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, false /* isLegacy */,
2260                     transactionId, request.calculateRequestDurationMs(mClock.elapsedRealtime()));
2261         }
2262 
2263         @Override
onOffloadStartOrUpdate(@onNull String interfaceName, @NonNull OffloadServiceInfo offloadServiceInfo)2264         public void onOffloadStartOrUpdate(@NonNull String interfaceName,
2265                 @NonNull OffloadServiceInfo offloadServiceInfo) {
2266             sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */);
2267         }
2268 
2269         @Override
onOffloadStop(@onNull String interfaceName, @NonNull OffloadServiceInfo offloadServiceInfo)2270         public void onOffloadStop(@NonNull String interfaceName,
2271                 @NonNull OffloadServiceInfo offloadServiceInfo) {
2272             sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */);
2273         }
2274 
getClientInfoOrLog(int transactionId)2275         private ClientInfo getClientInfoOrLog(int transactionId) {
2276             final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
2277             if (clientInfo == null) {
2278                 Log.e(TAG, String.format("Callback for service %d has no client", transactionId));
2279             }
2280             return clientInfo;
2281         }
2282 
getClientRequestIdOrLog(@onNull ClientInfo info, int transactionId)2283         private int getClientRequestIdOrLog(@NonNull ClientInfo info, int transactionId) {
2284             final int clientRequestId = info.getClientRequestId(transactionId);
2285             if (clientRequestId < 0) {
2286                 Log.e(TAG, String.format(
2287                         "Client request ID not found for service %d", transactionId));
2288             }
2289             return clientRequestId;
2290         }
2291     }
2292 
2293     private static class ConnectorArgs {
2294         @NonNull public final NsdServiceConnector connector;
2295         @NonNull public final INsdManagerCallback callback;
2296         public final boolean useJavaBackend;
2297         public final int uid;
2298 
ConnectorArgs(@onNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback, boolean useJavaBackend, int uid)2299         ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
2300                 boolean useJavaBackend, int uid) {
2301             this.connector = connector;
2302             this.callback = callback;
2303             this.useJavaBackend = useJavaBackend;
2304             this.uid = uid;
2305         }
2306     }
2307 
2308     @Override
connect(INsdManagerCallback cb, boolean useJavaBackend)2309     public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
2310         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
2311         final int uid = mDeps.getCallingUid();
2312         if (cb == null) {
2313             throw new IllegalArgumentException("Unknown client callback from uid=" + uid);
2314         }
2315         if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
2316         final INsdServiceConnector connector = new NsdServiceConnector();
2317         mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
2318                 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, uid)));
2319         return connector;
2320     }
2321 
2322     private static class ListenerArgs {
2323         public final NsdServiceConnector connector;
2324         public final NsdServiceInfo serviceInfo;
ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo)2325         ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
2326             this.connector = connector;
2327             this.serviceInfo = serviceInfo;
2328         }
2329     }
2330 
2331     private static class AdvertisingArgs {
2332         public final NsdServiceConnector connector;
2333         public final AdvertisingRequest advertisingRequest;
2334 
AdvertisingArgs(NsdServiceConnector connector, AdvertisingRequest advertisingRequest)2335         AdvertisingArgs(NsdServiceConnector connector, AdvertisingRequest advertisingRequest) {
2336             this.connector = connector;
2337             this.advertisingRequest = advertisingRequest;
2338         }
2339     }
2340 
2341     private static final class DiscoveryArgs {
2342         public final NsdServiceConnector connector;
2343         public final DiscoveryRequest discoveryRequest;
DiscoveryArgs(NsdServiceConnector connector, DiscoveryRequest discoveryRequest)2344         DiscoveryArgs(NsdServiceConnector connector, DiscoveryRequest discoveryRequest) {
2345             this.connector = connector;
2346             this.discoveryRequest = discoveryRequest;
2347         }
2348     }
2349 
2350     private class NsdServiceConnector extends INsdServiceConnector.Stub
2351             implements IBinder.DeathRecipient  {
2352 
2353         @Override
registerService(int listenerKey, AdvertisingRequest advertisingRequest)2354         public void registerService(int listenerKey, AdvertisingRequest advertisingRequest)
2355                 throws RemoteException {
2356             NsdManager.checkServiceInfoForRegistration(advertisingRequest.getServiceInfo());
2357             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2358                     NsdManager.REGISTER_SERVICE, 0, listenerKey,
2359                     new AdvertisingArgs(this, advertisingRequest)
2360             ));
2361         }
2362 
2363         @Override
unregisterService(int listenerKey)2364         public void unregisterService(int listenerKey) {
2365             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2366                     NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
2367                     new ListenerArgs(this, (NsdServiceInfo) null)));
2368         }
2369 
2370         @Override
discoverServices(int listenerKey, DiscoveryRequest discoveryRequest)2371         public void discoverServices(int listenerKey, DiscoveryRequest discoveryRequest) {
2372             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2373                     NsdManager.DISCOVER_SERVICES, 0, listenerKey,
2374                     new DiscoveryArgs(this, discoveryRequest)));
2375         }
2376 
2377         @Override
stopDiscovery(int listenerKey)2378         public void stopDiscovery(int listenerKey) {
2379             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_DISCOVERY,
2380                     0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null)));
2381         }
2382 
2383         @Override
resolveService(int listenerKey, NsdServiceInfo serviceInfo)2384         public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
2385             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2386                     NsdManager.RESOLVE_SERVICE, 0, listenerKey,
2387                     new ListenerArgs(this, serviceInfo)));
2388         }
2389 
2390         @Override
stopResolution(int listenerKey)2391         public void stopResolution(int listenerKey) {
2392             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_RESOLUTION,
2393                     0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null)));
2394         }
2395 
2396         @Override
registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo)2397         public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
2398             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2399                     NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
2400                     new ListenerArgs(this, serviceInfo)));
2401         }
2402 
2403         @Override
unregisterServiceInfoCallback(int listenerKey)2404         public void unregisterServiceInfoCallback(int listenerKey) {
2405             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2406                     NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
2407                     new ListenerArgs(this, (NsdServiceInfo) null)));
2408         }
2409 
2410         @Override
startDaemon()2411         public void startDaemon() {
2412             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.DAEMON_STARTUP,
2413                     new ListenerArgs(this, (NsdServiceInfo) null)));
2414         }
2415 
2416         @Override
binderDied()2417         public void binderDied() {
2418             mNsdStateMachine.sendMessage(
2419                     mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
2420 
2421         }
2422 
2423         @Override
registerOffloadEngine(String ifaceName, IOffloadEngine cb, @OffloadEngine.OffloadCapability long offloadCapabilities, @OffloadEngine.OffloadType long offloadTypes)2424         public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
2425                 @OffloadEngine.OffloadCapability long offloadCapabilities,
2426                 @OffloadEngine.OffloadType long offloadTypes) {
2427             checkOffloadEnginePermission(mContext);
2428             Objects.requireNonNull(ifaceName);
2429             Objects.requireNonNull(cb);
2430             mNsdStateMachine.sendMessage(
2431                     mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE,
2432                             new OffloadEngineInfo(cb, ifaceName, offloadCapabilities,
2433                                     offloadTypes)));
2434         }
2435 
2436         @Override
unregisterOffloadEngine(IOffloadEngine cb)2437         public void unregisterOffloadEngine(IOffloadEngine cb) {
2438             checkOffloadEnginePermission(mContext);
2439             Objects.requireNonNull(cb);
2440             mNsdStateMachine.sendMessage(
2441                     mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
2442         }
2443 
checkOffloadEnginePermission(Context context)2444         private static void checkOffloadEnginePermission(Context context) {
2445             if (!SdkLevel.isAtLeastT()) {
2446                 throw new SecurityException("API is not available in before API level 33");
2447             }
2448 
2449             final ArrayList<String> permissionsList = new ArrayList<>(Arrays.asList(NETWORK_STACK,
2450                     PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS));
2451 
2452             if (SdkLevel.isAtLeastV()) {
2453                 // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V.
2454                 permissionsList.add(REGISTER_NSD_OFFLOAD_ENGINE);
2455             } else if (SdkLevel.isAtLeastU()) {
2456                 // REGISTER_NSD_OFFLOAD_ENGINE cannot be backport to U. In U, check the DEVICE_POWER
2457                 // permission instead.
2458                 permissionsList.add(DEVICE_POWER);
2459             }
2460 
2461             if (PermissionUtils.hasAnyPermissionOf(context,
2462                     permissionsList.toArray(new String[0]))) {
2463                 return;
2464             }
2465             throw new SecurityException("Requires one of the following permissions: "
2466                     + String.join(", ", permissionsList) + ".");
2467         }
2468     }
2469 
sendNsdStateChangeBroadcast(boolean isEnabled)2470     private void sendNsdStateChangeBroadcast(boolean isEnabled) {
2471         final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
2472         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
2473         int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
2474         intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
2475         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2476     }
2477 
getUniqueId()2478     private int getUniqueId() {
2479         if (++mUniqueId == INVALID_ID) return ++mUniqueId;
2480         return mUniqueId;
2481     }
2482 
registerService(int transactionId, NsdServiceInfo service)2483     private boolean registerService(int transactionId, NsdServiceInfo service) {
2484         if (mMDnsManager == null) {
2485             Log.wtf(TAG, "registerService: mMDnsManager is null");
2486             return false;
2487         }
2488 
2489         if (DBG) {
2490             Log.d(TAG, "registerService: " + transactionId + " " + service);
2491         }
2492         String name = service.getServiceName();
2493         String type = service.getServiceType();
2494         int port = service.getPort();
2495         byte[] textRecord = service.getTxtRecord();
2496         final int registerInterface = getNetworkInterfaceIndex(service);
2497         if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
2498             Log.e(TAG, "Interface to register service on not found");
2499             return false;
2500         }
2501         return mMDnsManager.registerService(
2502                 transactionId, name, type, port, textRecord, registerInterface);
2503     }
2504 
unregisterService(int transactionId)2505     private boolean unregisterService(int transactionId) {
2506         if (mMDnsManager == null) {
2507             Log.wtf(TAG, "unregisterService: mMDnsManager is null");
2508             return false;
2509         }
2510         return mMDnsManager.stopOperation(transactionId);
2511     }
2512 
discoverServices(int transactionId, DiscoveryRequest discoveryRequest)2513     private boolean discoverServices(int transactionId, DiscoveryRequest discoveryRequest) {
2514         if (mMDnsManager == null) {
2515             Log.wtf(TAG, "discoverServices: mMDnsManager is null");
2516             return false;
2517         }
2518 
2519         final String type = discoveryRequest.getServiceType();
2520         final int discoverInterface = getNetworkInterfaceIndex(discoveryRequest);
2521         if (discoveryRequest.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
2522             Log.e(TAG, "Interface to discover service on not found");
2523             return false;
2524         }
2525         return mMDnsManager.discover(transactionId, type, discoverInterface);
2526     }
2527 
stopServiceDiscovery(int transactionId)2528     private boolean stopServiceDiscovery(int transactionId) {
2529         if (mMDnsManager == null) {
2530             Log.wtf(TAG, "stopServiceDiscovery: mMDnsManager is null");
2531             return false;
2532         }
2533         return mMDnsManager.stopOperation(transactionId);
2534     }
2535 
resolveService(int transactionId, NsdServiceInfo service)2536     private boolean resolveService(int transactionId, NsdServiceInfo service) {
2537         if (mMDnsManager == null) {
2538             Log.wtf(TAG, "resolveService: mMDnsManager is null");
2539             return false;
2540         }
2541         final String name = service.getServiceName();
2542         final String type = service.getServiceType();
2543         final int resolveInterface = getNetworkInterfaceIndex(service);
2544         if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
2545             Log.e(TAG, "Interface to resolve service on not found");
2546             return false;
2547         }
2548         return mMDnsManager.resolve(transactionId, name, type, "local.", resolveInterface);
2549     }
2550 
2551     /**
2552      * Guess the interface to use to resolve or discover a service on a specific network.
2553      *
2554      * This is an imperfect guess, as for example the network may be gone or not yet fully
2555      * registered. This is fine as failing is correct if the network is gone, and a client
2556      * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
2557      * this is to support the legacy mdnsresponder implementation, which historically resolved
2558      * services on an unspecified network.
2559      */
getNetworkInterfaceIndex(NsdServiceInfo serviceInfo)2560     private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
2561         final Network network = serviceInfo.getNetwork();
2562         if (network == null) {
2563             // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
2564             // provided by NsdService from discovery results, and the service was found on an
2565             // interface that has no app-usable Network).
2566             if (serviceInfo.getInterfaceIndex() != 0) {
2567                 return serviceInfo.getInterfaceIndex();
2568             }
2569             return IFACE_IDX_ANY;
2570         }
2571         return getNetworkInterfaceIndex(network);
2572     }
2573 
2574     /**
2575      * Returns the interface to use to discover a service on a specific network, or {@link
2576      * IFACE_IDX_ANY} if no network is specified.
2577      */
getNetworkInterfaceIndex(DiscoveryRequest discoveryRequest)2578     private int getNetworkInterfaceIndex(DiscoveryRequest discoveryRequest) {
2579         final Network network = discoveryRequest.getNetwork();
2580         if (network == null) {
2581             return IFACE_IDX_ANY;
2582         }
2583         return getNetworkInterfaceIndex(network);
2584     }
2585 
2586     /**
2587      * Returns the interface of a specific network, or {@link IFACE_IDX_ANY} if no interface is
2588      * associated with {@code network}.
2589      */
getNetworkInterfaceIndex(@onNull Network network)2590     private int getNetworkInterfaceIndex(@NonNull Network network) {
2591         String interfaceName = getNetworkInterfaceName(network);
2592         if (interfaceName == null) {
2593             return IFACE_IDX_ANY;
2594         }
2595         return getNetworkInterfaceIndexByName(interfaceName);
2596     }
2597 
getNetworkInterfaceName(@ullable Network network)2598     private String getNetworkInterfaceName(@Nullable Network network) {
2599         if (network == null) {
2600             return null;
2601         }
2602         final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
2603         if (cm == null) {
2604             Log.wtf(TAG, "No ConnectivityManager");
2605             return null;
2606         }
2607         final LinkProperties lp = cm.getLinkProperties(network);
2608         if (lp == null) {
2609             return null;
2610         }
2611         // Only resolve on non-stacked interfaces
2612         return lp.getInterfaceName();
2613     }
2614 
getNetworkInterfaceIndexByName(final String ifaceName)2615     private int getNetworkInterfaceIndexByName(final String ifaceName) {
2616         final NetworkInterface iface;
2617         try {
2618             iface = NetworkInterface.getByName(ifaceName);
2619         } catch (SocketException e) {
2620             Log.e(TAG, "Error querying interface", e);
2621             return IFACE_IDX_ANY;
2622         }
2623 
2624         if (iface == null) {
2625             Log.e(TAG, "Interface not found: " + ifaceName);
2626             return IFACE_IDX_ANY;
2627         }
2628 
2629         return iface.getIndex();
2630     }
2631 
stopResolveService(int transactionId)2632     private boolean stopResolveService(int transactionId) {
2633         if (mMDnsManager == null) {
2634             Log.wtf(TAG, "stopResolveService: mMDnsManager is null");
2635             return false;
2636         }
2637         return mMDnsManager.stopOperation(transactionId);
2638     }
2639 
getAddrInfo(int transactionId, String hostname, int interfaceIdx)2640     private boolean getAddrInfo(int transactionId, String hostname, int interfaceIdx) {
2641         if (mMDnsManager == null) {
2642             Log.wtf(TAG, "getAddrInfo: mMDnsManager is null");
2643             return false;
2644         }
2645         return mMDnsManager.getServiceAddress(transactionId, hostname, interfaceIdx);
2646     }
2647 
stopGetAddrInfo(int transactionId)2648     private boolean stopGetAddrInfo(int transactionId) {
2649         if (mMDnsManager == null) {
2650             Log.wtf(TAG, "stopGetAddrInfo: mMDnsManager is null");
2651             return false;
2652         }
2653         return mMDnsManager.stopOperation(transactionId);
2654     }
2655 
2656     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)2657     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2658         if (!PermissionUtils.hasDumpPermission(mContext, TAG, writer)) return;
2659 
2660         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
2661         // Dump state machine logs
2662         mNsdStateMachine.dump(fd, pw, args);
2663 
2664         // Dump clients
2665         pw.println();
2666         pw.println("Active clients:");
2667         pw.increaseIndent();
2668         HandlerUtils.runWithScissorsForDump(mNsdStateMachine.getHandler(), () -> {
2669             for (ClientInfo clientInfo : mClients.values()) {
2670                 pw.println(clientInfo.toString());
2671             }
2672         }, 10_000);
2673         pw.decreaseIndent();
2674 
2675         // Dump service and clients logs
2676         pw.println();
2677         pw.println("Logs:");
2678         pw.increaseIndent();
2679         mServiceLogs.reverseDump(pw);
2680         pw.decreaseIndent();
2681 
2682         //Dump DiscoveryManager
2683         pw.println();
2684         pw.println("DiscoveryManager:");
2685         pw.increaseIndent();
2686         HandlerUtils.runWithScissorsForDump(
2687                 mNsdStateMachine.getHandler(), () -> mMdnsDiscoveryManager.dump(pw), 10_000);
2688         pw.decreaseIndent();
2689     }
2690 
2691     private abstract static class ClientRequest {
2692         private final int mTransactionId;
2693         private final long mStartTimeMs;
2694         private int mFoundServiceCount = 0;
2695         private int mLostServiceCount = 0;
2696         private final Set<String> mServices = new ArraySet<>();
2697         private boolean mIsServiceFromCache = false;
2698         private int mSentQueryCount = NO_SENT_QUERY_COUNT;
2699 
ClientRequest(int transactionId, long startTimeMs)2700         private ClientRequest(int transactionId, long startTimeMs) {
2701             mTransactionId = transactionId;
2702             mStartTimeMs = startTimeMs;
2703         }
2704 
calculateRequestDurationMs(long stopTimeMs)2705         public long calculateRequestDurationMs(long stopTimeMs) {
2706             return stopTimeMs - mStartTimeMs;
2707         }
2708 
onServiceFound(String serviceName)2709         public void onServiceFound(String serviceName) {
2710             mFoundServiceCount++;
2711             if (mServices.size() <= MAX_SERVICES_COUNT_METRIC_PER_CLIENT) {
2712                 mServices.add(serviceName);
2713             }
2714         }
2715 
onServiceLost()2716         public void onServiceLost() {
2717             mLostServiceCount++;
2718         }
2719 
getFoundServiceCount()2720         public int getFoundServiceCount() {
2721             return mFoundServiceCount;
2722         }
2723 
getLostServiceCount()2724         public int getLostServiceCount() {
2725             return mLostServiceCount;
2726         }
2727 
getServicesCount()2728         public int getServicesCount() {
2729             return mServices.size();
2730         }
2731 
setServiceFromCache(boolean isServiceFromCache)2732         public void setServiceFromCache(boolean isServiceFromCache) {
2733             mIsServiceFromCache = isServiceFromCache;
2734         }
2735 
isServiceFromCache()2736         public boolean isServiceFromCache() {
2737             return mIsServiceFromCache;
2738         }
2739 
onQuerySent()2740         public void onQuerySent() {
2741             mSentQueryCount++;
2742         }
2743 
getSentQueryCount()2744         public int getSentQueryCount() {
2745             return mSentQueryCount;
2746         }
2747 
2748         @NonNull
2749         @Override
toString()2750         public String toString() {
2751             return getRequestDescriptor() + " {" + mTransactionId
2752                     + ", startTime " + mStartTimeMs
2753                     + ", foundServices " + mFoundServiceCount
2754                     + ", lostServices " + mLostServiceCount
2755                     + ", fromCache " + mIsServiceFromCache
2756                     + ", sentQueries " + mSentQueryCount
2757                     + "}";
2758         }
2759 
2760         @NonNull
getRequestDescriptor()2761         protected abstract String getRequestDescriptor();
2762     }
2763 
2764     private static class LegacyClientRequest extends ClientRequest {
2765         private final int mRequestCode;
2766 
LegacyClientRequest(int transactionId, int requestCode, long startTimeMs)2767         private LegacyClientRequest(int transactionId, int requestCode, long startTimeMs) {
2768             super(transactionId, startTimeMs);
2769             mRequestCode = requestCode;
2770         }
2771 
2772         @NonNull
2773         @Override
getRequestDescriptor()2774         protected String getRequestDescriptor() {
2775             return "Legacy (" + mRequestCode + ")";
2776         }
2777     }
2778 
2779     private abstract static class JavaBackendClientRequest extends ClientRequest {
2780         @Nullable
2781         private final Network mRequestedNetwork;
2782 
JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork, long startTimeMs)2783         private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork,
2784                 long startTimeMs) {
2785             super(transactionId, startTimeMs);
2786             mRequestedNetwork = requestedNetwork;
2787         }
2788 
2789         @Nullable
getRequestedNetwork()2790         public Network getRequestedNetwork() {
2791             return mRequestedNetwork;
2792         }
2793     }
2794 
2795     private static class AdvertiserClientRequest extends JavaBackendClientRequest {
2796         @NonNull
2797         private final String mServiceFullName;
2798 
AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork, @NonNull String serviceFullName, long startTimeMs)2799         private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
2800                 @NonNull String serviceFullName, long startTimeMs) {
2801             super(transactionId, requestedNetwork, startTimeMs);
2802             mServiceFullName = serviceFullName;
2803         }
2804 
2805         @NonNull
2806         @Override
getRequestDescriptor()2807         public String getRequestDescriptor() {
2808             return String.format("Advertiser: serviceFullName=%s, net=%s",
2809                     mServiceFullName, getRequestedNetwork());
2810         }
2811     }
2812 
2813     private static class DiscoveryManagerRequest extends JavaBackendClientRequest {
2814         @NonNull
2815         private final MdnsListener mListener;
2816 
DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener, @Nullable Network requestedNetwork, long startTimeMs)2817         private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener,
2818                 @Nullable Network requestedNetwork, long startTimeMs) {
2819             super(transactionId, requestedNetwork, startTimeMs);
2820             mListener = listener;
2821         }
2822 
2823         @NonNull
2824         @Override
getRequestDescriptor()2825         public String getRequestDescriptor() {
2826             return String.format("Discovery/%s, net=%s", mListener, getRequestedNetwork());
2827         }
2828     }
2829 
2830     /* Information tracked per client */
2831     private class ClientInfo {
2832 
2833         /**
2834          * Maximum number of requests (callbacks) for a client.
2835          *
2836          * 200 listeners should be more than enough for most use-cases: even if a client tries to
2837          * file callbacks for every service on a local network, there are generally much less than
2838          * 200 devices on a local network (a /24 only allows 255 IPv4 devices), and while some
2839          * devices may have multiple services, many devices do not advertise any.
2840          */
2841         private static final int MAX_LIMIT = 200;
2842         private final INsdManagerCallback mCb;
2843         /* Remembers a resolved service until getaddrinfo completes */
2844         private NsdServiceInfo mResolvedService;
2845 
2846         /* A map from client request ID (listenerKey) to the request */
2847         private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>();
2848 
2849         // The target SDK of this client < Build.VERSION_CODES.S
2850         private boolean mIsPreSClient = false;
2851         private final int mUid;
2852         // The flag of using java backend if the client's target SDK >= U
2853         private final boolean mUseJavaBackend;
2854         // Store client logs
2855         private final SharedLog mClientLogs;
2856         // Report the nsd metrics data
2857         private final NetworkNsdReportedMetrics mMetrics;
2858 
ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend, SharedLog sharedLog, NetworkNsdReportedMetrics metrics)2859         private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend,
2860                 SharedLog sharedLog, NetworkNsdReportedMetrics metrics) {
2861             mCb = cb;
2862             mUid = uid;
2863             mUseJavaBackend = useJavaBackend;
2864             mClientLogs = sharedLog;
2865             mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
2866             mMetrics = metrics;
2867         }
2868 
2869         @Override
toString()2870         public String toString() {
2871             StringBuilder sb = new StringBuilder();
2872             sb.append("mUid ").append(mUid).append(", ");
2873             sb.append("mResolvedService ").append(mResolvedService).append(", ");
2874             sb.append("mIsLegacy ").append(mIsPreSClient).append(", ");
2875             sb.append("mUseJavaBackend ").append(mUseJavaBackend).append(", ");
2876             sb.append("mClientRequests:\n");
2877             for (int i = 0; i < mClientRequests.size(); i++) {
2878                 int clientRequestId = mClientRequests.keyAt(i);
2879                 sb.append("  ").append(clientRequestId)
2880                         .append(": ").append(mClientRequests.valueAt(i).toString())
2881                         .append("\n");
2882             }
2883             return sb.toString();
2884         }
2885 
getUid()2886         public int getUid() {
2887             return mUid;
2888         }
2889 
isPreSClient()2890         private boolean isPreSClient() {
2891             return mIsPreSClient;
2892         }
2893 
setPreSClient()2894         private void setPreSClient() {
2895             mIsPreSClient = true;
2896         }
2897 
unregisterMdnsListenerFromRequest(ClientRequest request)2898         private MdnsListener unregisterMdnsListenerFromRequest(ClientRequest request) {
2899             final MdnsListener listener =
2900                     ((DiscoveryManagerRequest) request).mListener;
2901             mMdnsDiscoveryManager.unregisterListener(
2902                     listener.getListenedServiceType(), listener);
2903             return listener;
2904         }
2905 
2906         // Remove any pending requests from the global map when we get rid of a client,
2907         // and send cancellations to the daemon.
expungeAllRequests()2908         private void expungeAllRequests() {
2909             mClientLogs.log("Client unregistered. expungeAllRequests!");
2910             // TODO: to keep handler responsive, do not clean all requests for that client at once.
2911             for (int i = 0; i < mClientRequests.size(); i++) {
2912                 final int clientRequestId = mClientRequests.keyAt(i);
2913                 final ClientRequest request = mClientRequests.valueAt(i);
2914                 final int transactionId = request.mTransactionId;
2915                 mTransactionIdToClientInfoMap.remove(transactionId);
2916                 if (DBG) {
2917                     Log.d(TAG, "Terminating clientRequestId " + clientRequestId
2918                             + " transactionId " + transactionId
2919                             + " type " + mClientRequests.get(clientRequestId));
2920                 }
2921 
2922                 if (request instanceof DiscoveryManagerRequest) {
2923                     final MdnsListener listener = unregisterMdnsListenerFromRequest(request);
2924                     if (listener instanceof DiscoveryListener) {
2925                         mMetrics.reportServiceDiscoveryStop(false /* isLegacy */, transactionId,
2926                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2927                                 request.getFoundServiceCount(),
2928                                 request.getLostServiceCount(),
2929                                 request.getServicesCount(),
2930                                 request.getSentQueryCount(),
2931                                 request.isServiceFromCache());
2932                     } else if (listener instanceof ResolutionListener) {
2933                         mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId,
2934                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2935                                 request.getSentQueryCount());
2936                     } else if (listener instanceof ServiceInfoListener) {
2937                         mMetrics.reportServiceInfoCallbackUnregistered(transactionId,
2938                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2939                                 request.getFoundServiceCount(),
2940                                 request.getLostServiceCount(),
2941                                 request.isServiceFromCache(),
2942                                 request.getSentQueryCount());
2943                     }
2944                     continue;
2945                 }
2946 
2947                 if (request instanceof AdvertiserClientRequest) {
2948                     final AdvertiserMetrics metrics =
2949                             mAdvertiser.getAdvertiserMetrics(transactionId);
2950                     mAdvertiser.removeService(transactionId);
2951                     mMetrics.reportServiceUnregistration(false /* isLegacy */, transactionId,
2952                             request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2953                             metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2954                             metrics.mConflictDuringProbingCount,
2955                             metrics.mConflictAfterProbingCount);
2956                     continue;
2957                 }
2958 
2959                 if (!(request instanceof LegacyClientRequest)) {
2960                     throw new IllegalStateException("Unknown request type: " + request.getClass());
2961                 }
2962 
2963                 switch (((LegacyClientRequest) request).mRequestCode) {
2964                     case NsdManager.DISCOVER_SERVICES:
2965                         stopServiceDiscovery(transactionId);
2966                         mMetrics.reportServiceDiscoveryStop(true /* isLegacy */, transactionId,
2967                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2968                                 request.getFoundServiceCount(),
2969                                 request.getLostServiceCount(),
2970                                 request.getServicesCount(),
2971                                 NO_SENT_QUERY_COUNT,
2972                                 request.isServiceFromCache());
2973                         break;
2974                     case NsdManager.RESOLVE_SERVICE:
2975                         stopResolveService(transactionId);
2976                         mMetrics.reportServiceResolutionStop(true /* isLegacy */, transactionId,
2977                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2978                                 NO_SENT_QUERY_COUNT);
2979                         break;
2980                     case NsdManager.REGISTER_SERVICE:
2981                         unregisterService(transactionId);
2982                         mMetrics.reportServiceUnregistration(true /* isLegacy */, transactionId,
2983                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2984                                 NO_PACKET /* repliedRequestsCount */,
2985                                 NO_PACKET /* sentPacketCount */,
2986                                 0 /* conflictDuringProbingCount */,
2987                                 0 /* conflictAfterProbingCount */);
2988                         break;
2989                     default:
2990                         break;
2991                 }
2992             }
2993             mClientRequests.clear();
2994             updateMulticastLock();
2995         }
2996 
2997         /**
2998          * Returns true if this client has any Java backend request that requests one of the given
2999          * networks.
3000          */
hasAnyJavaBackendRequestForNetworks(@onNull ArraySet<Network> networks)3001         boolean hasAnyJavaBackendRequestForNetworks(@NonNull ArraySet<Network> networks) {
3002             for (int i = 0; i < mClientRequests.size(); i++) {
3003                 final ClientRequest req = mClientRequests.valueAt(i);
3004                 if (!(req instanceof JavaBackendClientRequest)) {
3005                     continue;
3006                 }
3007                 final Network reqNetwork = ((JavaBackendClientRequest) mClientRequests.valueAt(i))
3008                         .getRequestedNetwork();
3009                 if (MdnsUtils.isAnyNetworkMatched(reqNetwork, networks)) {
3010                     return true;
3011                 }
3012             }
3013             return false;
3014         }
3015 
3016         // mClientRequests is a sparse array of client request id -> ClientRequest.  For a given
3017         // transaction id, return the corresponding client request id.
getClientRequestId(final int transactionId)3018         private int getClientRequestId(final int transactionId) {
3019             for (int i = 0; i < mClientRequests.size(); i++) {
3020                 if (mClientRequests.valueAt(i).mTransactionId == transactionId) {
3021                     return mClientRequests.keyAt(i);
3022                 }
3023             }
3024             return -1;
3025         }
3026 
log(String message)3027         private void log(String message) {
3028             mClientLogs.log(message);
3029         }
3030 
isLegacyClientRequest(@onNull ClientRequest request)3031         private static boolean isLegacyClientRequest(@NonNull ClientRequest request) {
3032             return !(request instanceof DiscoveryManagerRequest)
3033                     && !(request instanceof AdvertiserClientRequest);
3034         }
3035 
onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest, ClientRequest request)3036         void onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest,
3037                 ClientRequest request) {
3038             mMetrics.reportServiceDiscoveryStarted(
3039                     isLegacyClientRequest(request), request.mTransactionId);
3040             try {
3041                 mCb.onDiscoverServicesStarted(listenerKey, discoveryRequest);
3042             } catch (RemoteException e) {
3043                 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
3044             }
3045         }
onDiscoverServicesFailedImmediately(int listenerKey, int error, boolean isLegacy)3046         void onDiscoverServicesFailedImmediately(int listenerKey, int error, boolean isLegacy) {
3047             onDiscoverServicesFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
3048                     0L /* durationMs */);
3049         }
3050 
onDiscoverServicesFailed(int listenerKey, int error, boolean isLegacy, int transactionId, long durationMs)3051         void onDiscoverServicesFailed(int listenerKey, int error, boolean isLegacy,
3052                 int transactionId, long durationMs) {
3053             mMetrics.reportServiceDiscoveryFailed(isLegacy, transactionId, durationMs);
3054             try {
3055                 mCb.onDiscoverServicesFailed(listenerKey, error);
3056             } catch (RemoteException e) {
3057                 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
3058             }
3059         }
3060 
onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request)3061         void onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request) {
3062             request.onServiceFound(info.getServiceName());
3063             try {
3064                 mCb.onServiceFound(listenerKey, info);
3065             } catch (RemoteException e) {
3066                 Log.e(TAG, "Error calling onServiceFound(", e);
3067             }
3068         }
3069 
onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request)3070         void onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request) {
3071             request.onServiceLost();
3072             try {
3073                 mCb.onServiceLost(listenerKey, info);
3074             } catch (RemoteException e) {
3075                 Log.e(TAG, "Error calling onServiceLost(", e);
3076             }
3077         }
3078 
onStopDiscoveryFailed(int listenerKey, int error)3079         void onStopDiscoveryFailed(int listenerKey, int error) {
3080             try {
3081                 mCb.onStopDiscoveryFailed(listenerKey, error);
3082             } catch (RemoteException e) {
3083                 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
3084             }
3085         }
3086 
onStopDiscoverySucceeded(int listenerKey, ClientRequest request)3087         void onStopDiscoverySucceeded(int listenerKey, ClientRequest request) {
3088             mMetrics.reportServiceDiscoveryStop(
3089                     isLegacyClientRequest(request),
3090                     request.mTransactionId,
3091                     request.calculateRequestDurationMs(mClock.elapsedRealtime()),
3092                     request.getFoundServiceCount(),
3093                     request.getLostServiceCount(),
3094                     request.getServicesCount(),
3095                     request.getSentQueryCount(),
3096                     request.isServiceFromCache());
3097             try {
3098                 mCb.onStopDiscoverySucceeded(listenerKey);
3099             } catch (RemoteException e) {
3100                 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
3101             }
3102         }
3103 
onRegisterServiceFailedImmediately(int listenerKey, int error, boolean isLegacy)3104         void onRegisterServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
3105             onRegisterServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
3106                     0L /* durationMs */);
3107         }
3108 
onRegisterServiceFailed(int listenerKey, int error, boolean isLegacy, int transactionId, long durationMs)3109         void onRegisterServiceFailed(int listenerKey, int error, boolean isLegacy,
3110                 int transactionId, long durationMs) {
3111             mMetrics.reportServiceRegistrationFailed(isLegacy, transactionId, durationMs);
3112             try {
3113                 mCb.onRegisterServiceFailed(listenerKey, error);
3114             } catch (RemoteException e) {
3115                 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
3116             }
3117         }
3118 
onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info, ClientRequest request)3119         void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info,
3120                 ClientRequest request) {
3121             mMetrics.reportServiceRegistrationSucceeded(isLegacyClientRequest(request),
3122                     request.mTransactionId,
3123                     request.calculateRequestDurationMs(mClock.elapsedRealtime()));
3124             try {
3125                 mCb.onRegisterServiceSucceeded(listenerKey, info);
3126             } catch (RemoteException e) {
3127                 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
3128             }
3129         }
3130 
onUnregisterServiceFailed(int listenerKey, int error)3131         void onUnregisterServiceFailed(int listenerKey, int error) {
3132             try {
3133                 mCb.onUnregisterServiceFailed(listenerKey, error);
3134             } catch (RemoteException e) {
3135                 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
3136             }
3137         }
3138 
onUnregisterServiceSucceeded(int listenerKey, ClientRequest request, AdvertiserMetrics metrics)3139         void onUnregisterServiceSucceeded(int listenerKey, ClientRequest request,
3140                 AdvertiserMetrics metrics) {
3141             mMetrics.reportServiceUnregistration(isLegacyClientRequest(request),
3142                     request.mTransactionId,
3143                     request.calculateRequestDurationMs(mClock.elapsedRealtime()),
3144                     metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
3145                     metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount);
3146             try {
3147                 mCb.onUnregisterServiceSucceeded(listenerKey);
3148             } catch (RemoteException e) {
3149                 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
3150             }
3151         }
3152 
onResolveServiceFailedImmediately(int listenerKey, int error, boolean isLegacy)3153         void onResolveServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
3154             onResolveServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
3155                     0L /* durationMs */);
3156         }
3157 
onResolveServiceFailed(int listenerKey, int error, boolean isLegacy, int transactionId, long durationMs)3158         void onResolveServiceFailed(int listenerKey, int error, boolean isLegacy,
3159                 int transactionId, long durationMs) {
3160             mMetrics.reportServiceResolutionFailed(isLegacy, transactionId, durationMs);
3161             try {
3162                 mCb.onResolveServiceFailed(listenerKey, error);
3163             } catch (RemoteException e) {
3164                 Log.e(TAG, "Error calling onResolveServiceFailed", e);
3165             }
3166         }
3167 
onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info, ClientRequest request)3168         void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info,
3169                 ClientRequest request) {
3170             mMetrics.reportServiceResolved(
3171                     isLegacyClientRequest(request),
3172                     request.mTransactionId,
3173                     request.calculateRequestDurationMs(mClock.elapsedRealtime()),
3174                     request.isServiceFromCache(),
3175                     request.getSentQueryCount());
3176             try {
3177                 mCb.onResolveServiceSucceeded(listenerKey, info);
3178             } catch (RemoteException e) {
3179                 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
3180             }
3181         }
3182 
onStopResolutionFailed(int listenerKey, int error)3183         void onStopResolutionFailed(int listenerKey, int error) {
3184             try {
3185                 mCb.onStopResolutionFailed(listenerKey, error);
3186             } catch (RemoteException e) {
3187                 Log.e(TAG, "Error calling onStopResolutionFailed", e);
3188             }
3189         }
3190 
onStopResolutionSucceeded(int listenerKey, ClientRequest request)3191         void onStopResolutionSucceeded(int listenerKey, ClientRequest request) {
3192             mMetrics.reportServiceResolutionStop(
3193                     isLegacyClientRequest(request),
3194                     request.mTransactionId,
3195                     request.calculateRequestDurationMs(mClock.elapsedRealtime()),
3196                     request.getSentQueryCount());
3197             try {
3198                 mCb.onStopResolutionSucceeded(listenerKey);
3199             } catch (RemoteException e) {
3200                 Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
3201             }
3202         }
3203 
onServiceInfoCallbackRegistrationFailed(int listenerKey, int error)3204         void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
3205             mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
3206             try {
3207                 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
3208             } catch (RemoteException e) {
3209                 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
3210             }
3211         }
3212 
onServiceInfoCallbackRegistered(int transactionId)3213         void onServiceInfoCallbackRegistered(int transactionId) {
3214             mMetrics.reportServiceInfoCallbackRegistered(transactionId);
3215         }
3216 
onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request)3217         void onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request) {
3218             request.onServiceFound(info.getServiceName());
3219             try {
3220                 mCb.onServiceUpdated(listenerKey, info);
3221             } catch (RemoteException e) {
3222                 Log.e(TAG, "Error calling onServiceUpdated", e);
3223             }
3224         }
3225 
onServiceUpdatedLost(int listenerKey, ClientRequest request)3226         void onServiceUpdatedLost(int listenerKey, ClientRequest request) {
3227             request.onServiceLost();
3228             try {
3229                 mCb.onServiceUpdatedLost(listenerKey);
3230             } catch (RemoteException e) {
3231                 Log.e(TAG, "Error calling onServiceUpdatedLost", e);
3232             }
3233         }
3234 
onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request)3235         void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) {
3236             mMetrics.reportServiceInfoCallbackUnregistered(
3237                     request.mTransactionId,
3238                     request.calculateRequestDurationMs(mClock.elapsedRealtime()),
3239                     request.getFoundServiceCount(),
3240                     request.getLostServiceCount(),
3241                     request.isServiceFromCache(),
3242                     request.getSentQueryCount());
3243             try {
3244                 mCb.onServiceInfoCallbackUnregistered(listenerKey);
3245             } catch (RemoteException e) {
3246                 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
3247             }
3248         }
3249     }
3250 }
3251