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