• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import static android.net.ConnectivityManager.NETID_UNSET;
20 import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
21 import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
22 import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
23 import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
24 
25 import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
26 import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
27 
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.net.ConnectivityManager;
33 import android.net.INetd;
34 import android.net.InetAddresses;
35 import android.net.LinkProperties;
36 import android.net.Network;
37 import android.net.mdns.aidl.DiscoveryInfo;
38 import android.net.mdns.aidl.GetAddressInfo;
39 import android.net.mdns.aidl.IMDnsEventListener;
40 import android.net.mdns.aidl.RegistrationInfo;
41 import android.net.mdns.aidl.ResolutionInfo;
42 import android.net.nsd.INsdManager;
43 import android.net.nsd.INsdManagerCallback;
44 import android.net.nsd.INsdServiceConnector;
45 import android.net.nsd.MDnsManager;
46 import android.net.nsd.NsdManager;
47 import android.net.nsd.NsdServiceInfo;
48 import android.os.Binder;
49 import android.os.Handler;
50 import android.os.HandlerThread;
51 import android.os.IBinder;
52 import android.os.Looper;
53 import android.os.Message;
54 import android.os.RemoteException;
55 import android.os.UserHandle;
56 import android.text.TextUtils;
57 import android.util.Log;
58 import android.util.Pair;
59 import android.util.SparseArray;
60 
61 import com.android.internal.annotations.VisibleForTesting;
62 import com.android.internal.util.IndentingPrintWriter;
63 import com.android.internal.util.State;
64 import com.android.internal.util.StateMachine;
65 import com.android.net.module.util.DeviceConfigUtils;
66 import com.android.net.module.util.InetAddressUtils;
67 import com.android.net.module.util.PermissionUtils;
68 import com.android.net.module.util.SharedLog;
69 import com.android.server.connectivity.mdns.ExecutorProvider;
70 import com.android.server.connectivity.mdns.MdnsAdvertiser;
71 import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
72 import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
73 import com.android.server.connectivity.mdns.MdnsSearchOptions;
74 import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
75 import com.android.server.connectivity.mdns.MdnsServiceInfo;
76 import com.android.server.connectivity.mdns.MdnsSocketProvider;
77 import com.android.server.connectivity.mdns.util.MdnsUtils;
78 
79 import java.io.FileDescriptor;
80 import java.io.PrintWriter;
81 import java.net.Inet6Address;
82 import java.net.InetAddress;
83 import java.net.NetworkInterface;
84 import java.net.SocketException;
85 import java.net.UnknownHostException;
86 import java.util.ArrayList;
87 import java.util.Arrays;
88 import java.util.HashMap;
89 import java.util.List;
90 import java.util.Map;
91 import java.util.regex.Matcher;
92 import java.util.regex.Pattern;
93 
94 /**
95  * Network Service Discovery Service handles remote service discovery operation requests by
96  * implementing the INsdManager interface.
97  *
98  * @hide
99  */
100 public class NsdService extends INsdManager.Stub {
101     private static final String TAG = "NsdService";
102     private static final String MDNS_TAG = "mDnsConnector";
103     /**
104      * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder
105      * implementation.
106      */
107     private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
108     private static final String LOCAL_DOMAIN_NAME = "local";
109 
110     /**
111      * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
112      * implementation.
113      */
114     private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
115 
116     /**
117      * Comma-separated list of type:flag mappings indicating the flags to use to allowlist
118      * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type.
119      *
120      * For example _mytype._tcp.local and _othertype._tcp.local would be configured with:
121      * _mytype._tcp:mytype,_othertype._tcp.local:othertype
122      *
123      * In which case the flags:
124      * "mdns_discovery_manager_allowlist_mytype_version",
125      * "mdns_advertiser_allowlist_mytype_version",
126      * "mdns_discovery_manager_allowlist_othertype_version",
127      * "mdns_advertiser_allowlist_othertype_version"
128      * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
129      * be read with
130      * {@link DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean)}.
131      *
132      * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
133      * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
134      * @see #MDNS_ALLOWLIST_FLAG_SUFFIX
135      */
136     private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags";
137 
138     private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX =
139             "mdns_discovery_manager_allowlist_";
140     private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX =
141             "mdns_advertiser_allowlist_";
142     private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
143 
144     public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
145     private static final long CLEANUP_DELAY_MS = 10000;
146     private static final int IFACE_IDX_ANY = 0;
147     private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
148 
149     private final Context mContext;
150     private final NsdStateMachine mNsdStateMachine;
151     private final MDnsManager mMDnsManager;
152     private final MDnsEventCallback mMDnsEventCallback;
153     @NonNull
154     private final Dependencies mDeps;
155     @NonNull
156     private final MdnsMultinetworkSocketClient mMdnsSocketClient;
157     @NonNull
158     private final MdnsDiscoveryManager mMdnsDiscoveryManager;
159     @NonNull
160     private final MdnsSocketProvider mMdnsSocketProvider;
161     @NonNull
162     private final MdnsAdvertiser mAdvertiser;
163     private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
164     // WARNING : Accessing these values in any thread is not safe, it must only be changed in the
165     // state machine thread. If change this outside state machine, it will need to introduce
166     // synchronization.
167     private boolean mIsDaemonStarted = false;
168     private boolean mIsMonitoringSocketsStarted = false;
169 
170     /**
171      * Clients receiving asynchronous messages
172      */
173     private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
174 
175     /* A map from unique id to client info */
176     private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
177 
178     private final long mCleanupDelayMs;
179 
180     private static final int INVALID_ID = 0;
181     private int mUniqueId = 1;
182     // The count of the connected legacy clients.
183     private int mLegacyClientCount = 0;
184     // The number of client that ever connected.
185     private int mClientNumberId = 1;
186 
187     private static class MdnsListener implements MdnsServiceBrowserListener {
188         protected final int mClientId;
189         protected final int mTransactionId;
190         @NonNull
191         protected final NsdServiceInfo mReqServiceInfo;
192         @NonNull
193         protected final String mListenedServiceType;
194 
MdnsListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenedServiceType)195         MdnsListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
196                 @NonNull String listenedServiceType) {
197             mClientId = clientId;
198             mTransactionId = transactionId;
199             mReqServiceInfo = reqServiceInfo;
200             mListenedServiceType = listenedServiceType;
201         }
202 
203         @NonNull
getListenedServiceType()204         public String getListenedServiceType() {
205             return mListenedServiceType;
206         }
207 
208         @Override
onServiceFound(@onNull MdnsServiceInfo serviceInfo)209         public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) { }
210 
211         @Override
onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)212         public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
213 
214         @Override
onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)215         public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
216 
217         @Override
onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo)218         public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) { }
219 
220         @Override
onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)221         public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
222 
223         @Override
onSearchStoppedWithError(int error)224         public void onSearchStoppedWithError(int error) { }
225 
226         @Override
onSearchFailedToStart()227         public void onSearchFailedToStart() { }
228 
229         @Override
onDiscoveryQuerySent(@onNull List<String> subtypes, int transactionId)230         public void onDiscoveryQuerySent(@NonNull List<String> subtypes, int transactionId) { }
231 
232         @Override
onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode)233         public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
234     }
235 
236     private class DiscoveryListener extends MdnsListener {
237 
DiscoveryListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType)238         DiscoveryListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
239                 @NonNull String listenServiceType) {
240             super(clientId, transactionId, reqServiceInfo, listenServiceType);
241         }
242 
243         @Override
onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo)244         public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) {
245             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
246                     NsdManager.SERVICE_FOUND,
247                     new MdnsEvent(mClientId, serviceInfo));
248         }
249 
250         @Override
onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)251         public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
252             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
253                     NsdManager.SERVICE_LOST,
254                     new MdnsEvent(mClientId, serviceInfo));
255         }
256     }
257 
258     private class ResolutionListener extends MdnsListener {
259 
ResolutionListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType)260         ResolutionListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
261                 @NonNull String listenServiceType) {
262             super(clientId, transactionId, reqServiceInfo, listenServiceType);
263         }
264 
265         @Override
onServiceFound(MdnsServiceInfo serviceInfo)266         public void onServiceFound(MdnsServiceInfo serviceInfo) {
267             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
268                     NsdManager.RESOLVE_SERVICE_SUCCEEDED,
269                     new MdnsEvent(mClientId, serviceInfo));
270         }
271     }
272 
273     private class ServiceInfoListener extends MdnsListener {
274 
ServiceInfoListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType)275         ServiceInfoListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
276                 @NonNull String listenServiceType) {
277             super(clientId, transactionId, reqServiceInfo, listenServiceType);
278         }
279 
280         @Override
onServiceFound(@onNull MdnsServiceInfo serviceInfo)281         public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) {
282             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
283                     NsdManager.SERVICE_UPDATED,
284                     new MdnsEvent(mClientId, serviceInfo));
285         }
286 
287         @Override
onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)288         public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
289             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
290                     NsdManager.SERVICE_UPDATED,
291                     new MdnsEvent(mClientId, serviceInfo));
292         }
293 
294         @Override
onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)295         public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
296             mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
297                     NsdManager.SERVICE_UPDATED_LOST,
298                     new MdnsEvent(mClientId, serviceInfo));
299         }
300     }
301 
302     /**
303      * Data class of mdns service callback information.
304      */
305     private static class MdnsEvent {
306         final int mClientId;
307         @NonNull
308         final MdnsServiceInfo mMdnsServiceInfo;
309 
MdnsEvent(int clientId, @NonNull MdnsServiceInfo mdnsServiceInfo)310         MdnsEvent(int clientId, @NonNull MdnsServiceInfo mdnsServiceInfo) {
311             mClientId = clientId;
312             mMdnsServiceInfo = mdnsServiceInfo;
313         }
314     }
315 
316     private class NsdStateMachine extends StateMachine {
317 
318         private final DefaultState mDefaultState = new DefaultState();
319         private final EnabledState mEnabledState = new EnabledState();
320 
321         @Override
getWhatToString(int what)322         protected String getWhatToString(int what) {
323             return NsdManager.nameOf(what);
324         }
325 
maybeStartDaemon()326         private void maybeStartDaemon() {
327             if (mIsDaemonStarted) {
328                 if (DBG) Log.d(TAG, "Daemon is already started.");
329                 return;
330             }
331             mMDnsManager.registerEventListener(mMDnsEventCallback);
332             mMDnsManager.startDaemon();
333             mIsDaemonStarted = true;
334             maybeScheduleStop();
335             mServiceLogs.log("Start mdns_responder daemon");
336         }
337 
maybeStopDaemon()338         private void maybeStopDaemon() {
339             if (!mIsDaemonStarted) {
340                 if (DBG) Log.d(TAG, "Daemon has not been started.");
341                 return;
342             }
343             mMDnsManager.unregisterEventListener(mMDnsEventCallback);
344             mMDnsManager.stopDaemon();
345             mIsDaemonStarted = false;
346             mServiceLogs.log("Stop mdns_responder daemon");
347         }
348 
isAnyRequestActive()349         private boolean isAnyRequestActive() {
350             return mIdToClientInfoMap.size() != 0;
351         }
352 
scheduleStop()353         private void scheduleStop() {
354             sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
355         }
maybeScheduleStop()356         private void maybeScheduleStop() {
357             // The native daemon should stay alive and can't be cleanup
358             // if any legacy client connected.
359             if (!isAnyRequestActive() && mLegacyClientCount == 0) {
360                 scheduleStop();
361             }
362         }
363 
cancelStop()364         private void cancelStop() {
365             this.removeMessages(NsdManager.DAEMON_CLEANUP);
366         }
367 
maybeStartMonitoringSockets()368         private void maybeStartMonitoringSockets() {
369             if (mIsMonitoringSocketsStarted) {
370                 if (DBG) Log.d(TAG, "Socket monitoring is already started.");
371                 return;
372             }
373 
374             mMdnsSocketProvider.startMonitoringSockets();
375             mIsMonitoringSocketsStarted = true;
376         }
377 
maybeStopMonitoringSocketsIfNoActiveRequest()378         private void maybeStopMonitoringSocketsIfNoActiveRequest() {
379             if (!mIsMonitoringSocketsStarted) return;
380             if (isAnyRequestActive()) return;
381 
382             mMdnsSocketProvider.requestStopWhenInactive();
383             mIsMonitoringSocketsStarted = false;
384         }
385 
NsdStateMachine(String name, Handler handler)386         NsdStateMachine(String name, Handler handler) {
387             super(name, handler);
388             addState(mDefaultState);
389                 addState(mEnabledState, mDefaultState);
390             State initialState = mEnabledState;
391             setInitialState(initialState);
392             setLogRecSize(25);
393         }
394 
395         class DefaultState extends State {
396             @Override
processMessage(Message msg)397             public boolean processMessage(Message msg) {
398                 final ClientInfo cInfo;
399                 final int clientId = msg.arg2;
400                 switch (msg.what) {
401                     case NsdManager.REGISTER_CLIENT:
402                         final ConnectorArgs arg = (ConnectorArgs) msg.obj;
403                         final INsdManagerCallback cb = arg.callback;
404                         try {
405                             cb.asBinder().linkToDeath(arg.connector, 0);
406                             final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
407                             cInfo = new ClientInfo(cb, arg.useJavaBackend,
408                                     mServiceLogs.forSubComponent(tag));
409                             mClients.put(arg.connector, cInfo);
410                         } catch (RemoteException e) {
411                             Log.w(TAG, "Client " + clientId + " has already died");
412                         }
413                         break;
414                     case NsdManager.UNREGISTER_CLIENT:
415                         final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
416                         cInfo = mClients.remove(connector);
417                         if (cInfo != null) {
418                             cInfo.expungeAllRequests();
419                             if (cInfo.isPreSClient()) {
420                                 mLegacyClientCount -= 1;
421                             }
422                         }
423                         maybeStopMonitoringSocketsIfNoActiveRequest();
424                         maybeScheduleStop();
425                         break;
426                     case NsdManager.DISCOVER_SERVICES:
427                         cInfo = getClientInfoForReply(msg);
428                         if (cInfo != null) {
429                             cInfo.onDiscoverServicesFailed(
430                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
431                         }
432                        break;
433                     case NsdManager.STOP_DISCOVERY:
434                         cInfo = getClientInfoForReply(msg);
435                         if (cInfo != null) {
436                             cInfo.onStopDiscoveryFailed(
437                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
438                         }
439                         break;
440                     case NsdManager.REGISTER_SERVICE:
441                         cInfo = getClientInfoForReply(msg);
442                         if (cInfo != null) {
443                             cInfo.onRegisterServiceFailed(
444                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
445                         }
446                         break;
447                     case NsdManager.UNREGISTER_SERVICE:
448                         cInfo = getClientInfoForReply(msg);
449                         if (cInfo != null) {
450                             cInfo.onUnregisterServiceFailed(
451                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
452                         }
453                         break;
454                     case NsdManager.RESOLVE_SERVICE:
455                         cInfo = getClientInfoForReply(msg);
456                         if (cInfo != null) {
457                             cInfo.onResolveServiceFailed(
458                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
459                         }
460                         break;
461                     case NsdManager.STOP_RESOLUTION:
462                         cInfo = getClientInfoForReply(msg);
463                         if (cInfo != null) {
464                             cInfo.onStopResolutionFailed(
465                                     clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
466                         }
467                         break;
468                     case NsdManager.REGISTER_SERVICE_CALLBACK:
469                         cInfo = getClientInfoForReply(msg);
470                         if (cInfo != null) {
471                             cInfo.onServiceInfoCallbackRegistrationFailed(
472                                     clientId, NsdManager.FAILURE_BAD_PARAMETERS);
473                         }
474                         break;
475                     case NsdManager.DAEMON_CLEANUP:
476                         maybeStopDaemon();
477                         break;
478                     // This event should be only sent by the legacy (target SDK < S) clients.
479                     // Mark the sending client as legacy.
480                     case NsdManager.DAEMON_STARTUP:
481                         cInfo = getClientInfoForReply(msg);
482                         if (cInfo != null) {
483                             cancelStop();
484                             cInfo.setPreSClient();
485                             mLegacyClientCount += 1;
486                             maybeStartDaemon();
487                         }
488                         break;
489                     default:
490                         Log.e(TAG, "Unhandled " + msg);
491                         return NOT_HANDLED;
492                 }
493                 return HANDLED;
494             }
495 
getClientInfoForReply(Message msg)496             private ClientInfo getClientInfoForReply(Message msg) {
497                 final ListenerArgs args = (ListenerArgs) msg.obj;
498                 return mClients.get(args.connector);
499             }
500         }
501 
502         class EnabledState extends State {
503             @Override
enter()504             public void enter() {
505                 sendNsdStateChangeBroadcast(true);
506             }
507 
508             @Override
exit()509             public void exit() {
510                 // TODO: it is incorrect to stop the daemon without expunging all requests
511                 // and sending error callbacks to clients.
512                 scheduleStop();
513             }
514 
requestLimitReached(ClientInfo clientInfo)515             private boolean requestLimitReached(ClientInfo clientInfo) {
516                 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) {
517                     if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
518                     return true;
519                 }
520                 return false;
521             }
522 
storeLegacyRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what)523             private void storeLegacyRequestMap(int clientId, int globalId, ClientInfo clientInfo,
524                     int what) {
525                 clientInfo.mClientRequests.put(clientId, new LegacyClientRequest(globalId, what));
526                 mIdToClientInfoMap.put(globalId, clientInfo);
527                 // Remove the cleanup event because here comes a new request.
528                 cancelStop();
529             }
530 
storeAdvertiserRequestMap(int clientId, int globalId, ClientInfo clientInfo)531             private void storeAdvertiserRequestMap(int clientId, int globalId,
532                     ClientInfo clientInfo) {
533                 clientInfo.mClientRequests.put(clientId, new AdvertiserClientRequest(globalId));
534                 mIdToClientInfoMap.put(globalId, clientInfo);
535             }
536 
removeRequestMap(int clientId, int globalId, ClientInfo clientInfo)537             private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
538                 final ClientRequest existing = clientInfo.mClientRequests.get(clientId);
539                 if (existing == null) return;
540                 clientInfo.mClientRequests.remove(clientId);
541                 mIdToClientInfoMap.remove(globalId);
542 
543                 if (existing instanceof LegacyClientRequest) {
544                     maybeScheduleStop();
545                 } else {
546                     maybeStopMonitoringSocketsIfNoActiveRequest();
547                 }
548             }
549 
storeDiscoveryManagerRequestMap(int clientId, int globalId, MdnsListener listener, ClientInfo clientInfo)550             private void storeDiscoveryManagerRequestMap(int clientId, int globalId,
551                     MdnsListener listener, ClientInfo clientInfo) {
552                 clientInfo.mClientRequests.put(clientId,
553                         new DiscoveryManagerRequest(globalId, listener));
554                 mIdToClientInfoMap.put(globalId, clientInfo);
555             }
556 
557             /**
558              * Truncate a service name to up to 63 UTF-8 bytes.
559              *
560              * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating
561              * names used in registerService follows historical behavior (see mdnsresponder
562              * handle_regservice_request).
563              */
564             @NonNull
truncateServiceName(@onNull String originalName)565             private String truncateServiceName(@NonNull String originalName) {
566                 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH);
567             }
568 
stopDiscoveryManagerRequest(ClientRequest request, int clientId, int id, ClientInfo clientInfo)569             private void stopDiscoveryManagerRequest(ClientRequest request, int clientId, int id,
570                     ClientInfo clientInfo) {
571                 clientInfo.unregisterMdnsListenerFromRequest(request);
572                 removeRequestMap(clientId, id, clientInfo);
573             }
574 
575             @Override
processMessage(Message msg)576             public boolean processMessage(Message msg) {
577                 final ClientInfo clientInfo;
578                 final int id;
579                 final int clientId = msg.arg2;
580                 final ListenerArgs args;
581                 switch (msg.what) {
582                     case NsdManager.DISCOVER_SERVICES: {
583                         if (DBG) Log.d(TAG, "Discover services");
584                         args = (ListenerArgs) msg.obj;
585                         clientInfo = mClients.get(args.connector);
586                         // If the binder death notification for a INsdManagerCallback was received
587                         // before any calls are received by NsdService, the clientInfo would be
588                         // cleared and cause NPE. Add a null check here to prevent this corner case.
589                         if (clientInfo == null) {
590                             Log.e(TAG, "Unknown connector in discovery");
591                             break;
592                         }
593 
594                         if (requestLimitReached(clientInfo)) {
595                             clientInfo.onDiscoverServicesFailed(
596                                     clientId, NsdManager.FAILURE_MAX_LIMIT);
597                             break;
598                         }
599 
600                         final NsdServiceInfo info = args.serviceInfo;
601                         id = getUniqueId();
602                         final Pair<String, String> typeAndSubtype =
603                                 parseTypeAndSubtype(info.getServiceType());
604                         final String serviceType = typeAndSubtype == null
605                                 ? null : typeAndSubtype.first;
606                         if (clientInfo.mUseJavaBackend
607                                 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
608                                 || useDiscoveryManagerForType(serviceType)) {
609                             if (serviceType == null) {
610                                 clientInfo.onDiscoverServicesFailed(clientId,
611                                         NsdManager.FAILURE_INTERNAL_ERROR);
612                                 break;
613                             }
614 
615                             final String listenServiceType = serviceType + ".local";
616                             maybeStartMonitoringSockets();
617                             final MdnsListener listener =
618                                     new DiscoveryListener(clientId, id, info, listenServiceType);
619                             final MdnsSearchOptions.Builder optionsBuilder =
620                                     MdnsSearchOptions.newBuilder()
621                                             .setNetwork(info.getNetwork())
622                                             .setRemoveExpiredService(true)
623                                             .setIsPassiveMode(true);
624                             if (typeAndSubtype.second != null) {
625                                 // The parsing ensures subtype starts with an underscore.
626                                 // MdnsSearchOptions expects the underscore to not be present.
627                                 optionsBuilder.addSubtype(typeAndSubtype.second.substring(1));
628                             }
629                             mMdnsDiscoveryManager.registerListener(
630                                     listenServiceType, listener, optionsBuilder.build());
631                             storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
632                             clientInfo.onDiscoverServicesStarted(clientId, info);
633                             clientInfo.log("Register a DiscoveryListener " + id
634                                     + " for service type:" + listenServiceType);
635                         } else {
636                             maybeStartDaemon();
637                             if (discoverServices(id, info)) {
638                                 if (DBG) {
639                                     Log.d(TAG, "Discover " + msg.arg2 + " " + id
640                                             + info.getServiceType());
641                                 }
642                                 storeLegacyRequestMap(clientId, id, clientInfo, msg.what);
643                                 clientInfo.onDiscoverServicesStarted(clientId, info);
644                             } else {
645                                 stopServiceDiscovery(id);
646                                 clientInfo.onDiscoverServicesFailed(clientId,
647                                         NsdManager.FAILURE_INTERNAL_ERROR);
648                             }
649                         }
650                         break;
651                     }
652                     case NsdManager.STOP_DISCOVERY: {
653                         if (DBG) Log.d(TAG, "Stop service discovery");
654                         args = (ListenerArgs) msg.obj;
655                         clientInfo = mClients.get(args.connector);
656                         // If the binder death notification for a INsdManagerCallback was received
657                         // before any calls are received by NsdService, the clientInfo would be
658                         // cleared and cause NPE. Add a null check here to prevent this corner case.
659                         if (clientInfo == null) {
660                             Log.e(TAG, "Unknown connector in stop discovery");
661                             break;
662                         }
663 
664                         final ClientRequest request = clientInfo.mClientRequests.get(clientId);
665                         if (request == null) {
666                             Log.e(TAG, "Unknown client request in STOP_DISCOVERY");
667                             break;
668                         }
669                         id = request.mGlobalId;
670                         // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
671                         // point, so this needs to check the type of the original request to
672                         // unregister instead of looking at the flag value.
673                         if (request instanceof DiscoveryManagerRequest) {
674                             stopDiscoveryManagerRequest(request, clientId, id, clientInfo);
675                             clientInfo.onStopDiscoverySucceeded(clientId);
676                             clientInfo.log("Unregister the DiscoveryListener " + id);
677                         } else {
678                             removeRequestMap(clientId, id, clientInfo);
679                             if (stopServiceDiscovery(id)) {
680                                 clientInfo.onStopDiscoverySucceeded(clientId);
681                             } else {
682                                 clientInfo.onStopDiscoveryFailed(
683                                         clientId, NsdManager.FAILURE_INTERNAL_ERROR);
684                             }
685                         }
686                         break;
687                     }
688                     case NsdManager.REGISTER_SERVICE: {
689                         if (DBG) Log.d(TAG, "Register service");
690                         args = (ListenerArgs) msg.obj;
691                         clientInfo = mClients.get(args.connector);
692                         // If the binder death notification for a INsdManagerCallback was received
693                         // before any calls are received by NsdService, the clientInfo would be
694                         // cleared and cause NPE. Add a null check here to prevent this corner case.
695                         if (clientInfo == null) {
696                             Log.e(TAG, "Unknown connector in registration");
697                             break;
698                         }
699 
700                         if (requestLimitReached(clientInfo)) {
701                             clientInfo.onRegisterServiceFailed(
702                                     clientId, NsdManager.FAILURE_MAX_LIMIT);
703                             break;
704                         }
705 
706                         id = getUniqueId();
707                         final NsdServiceInfo serviceInfo = args.serviceInfo;
708                         final String serviceType = serviceInfo.getServiceType();
709                         final Pair<String, String> typeSubtype = parseTypeAndSubtype(serviceType);
710                         final String registerServiceType = typeSubtype == null
711                                 ? null : typeSubtype.first;
712                         if (clientInfo.mUseJavaBackend
713                                 || mDeps.isMdnsAdvertiserEnabled(mContext)
714                                 || useAdvertiserForType(registerServiceType)) {
715                             if (registerServiceType == null) {
716                                 Log.e(TAG, "Invalid service type: " + serviceType);
717                                 clientInfo.onRegisterServiceFailed(clientId,
718                                         NsdManager.FAILURE_INTERNAL_ERROR);
719                                 break;
720                             }
721                             serviceInfo.setServiceType(registerServiceType);
722                             serviceInfo.setServiceName(truncateServiceName(
723                                     serviceInfo.getServiceName()));
724 
725                             maybeStartMonitoringSockets();
726                             // TODO: pass in the subtype as well. Including the subtype in the
727                             // service type would generate service instance names like
728                             // Name._subtype._sub._type._tcp, which is incorrect
729                             // (it should be Name._type._tcp).
730                             mAdvertiser.addService(id, serviceInfo, typeSubtype.second);
731                             storeAdvertiserRequestMap(clientId, id, clientInfo);
732                         } else {
733                             maybeStartDaemon();
734                             if (registerService(id, serviceInfo)) {
735                                 if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
736                                 storeLegacyRequestMap(clientId, id, clientInfo, msg.what);
737                                 // Return success after mDns reports success
738                             } else {
739                                 unregisterService(id);
740                                 clientInfo.onRegisterServiceFailed(
741                                         clientId, NsdManager.FAILURE_INTERNAL_ERROR);
742                             }
743 
744                         }
745                         break;
746                     }
747                     case NsdManager.UNREGISTER_SERVICE: {
748                         if (DBG) Log.d(TAG, "unregister service");
749                         args = (ListenerArgs) msg.obj;
750                         clientInfo = mClients.get(args.connector);
751                         // If the binder death notification for a INsdManagerCallback was received
752                         // before any calls are received by NsdService, the clientInfo would be
753                         // cleared and cause NPE. Add a null check here to prevent this corner case.
754                         if (clientInfo == null) {
755                             Log.e(TAG, "Unknown connector in unregistration");
756                             break;
757                         }
758                         final ClientRequest request = clientInfo.mClientRequests.get(clientId);
759                         if (request == null) {
760                             Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE");
761                             break;
762                         }
763                         id = request.mGlobalId;
764                         removeRequestMap(clientId, id, clientInfo);
765 
766                         // Note isMdnsAdvertiserEnabled may have changed to false at this point,
767                         // so this needs to check the type of the original request to unregister
768                         // instead of looking at the flag value.
769                         if (request instanceof AdvertiserClientRequest) {
770                             mAdvertiser.removeService(id);
771                             clientInfo.onUnregisterServiceSucceeded(clientId);
772                         } else {
773                             if (unregisterService(id)) {
774                                 clientInfo.onUnregisterServiceSucceeded(clientId);
775                             } else {
776                                 clientInfo.onUnregisterServiceFailed(
777                                         clientId, NsdManager.FAILURE_INTERNAL_ERROR);
778                             }
779                         }
780                         break;
781                     }
782                     case NsdManager.RESOLVE_SERVICE: {
783                         if (DBG) Log.d(TAG, "Resolve service");
784                         args = (ListenerArgs) msg.obj;
785                         clientInfo = mClients.get(args.connector);
786                         // If the binder death notification for a INsdManagerCallback was received
787                         // before any calls are received by NsdService, the clientInfo would be
788                         // cleared and cause NPE. Add a null check here to prevent this corner case.
789                         if (clientInfo == null) {
790                             Log.e(TAG, "Unknown connector in resolution");
791                             break;
792                         }
793 
794                         final NsdServiceInfo info = args.serviceInfo;
795                         id = getUniqueId();
796                         final Pair<String, String> typeSubtype =
797                                 parseTypeAndSubtype(info.getServiceType());
798                         final String serviceType = typeSubtype == null
799                                 ? null : typeSubtype.first;
800                         if (clientInfo.mUseJavaBackend
801                                 ||  mDeps.isMdnsDiscoveryManagerEnabled(mContext)
802                                 || useDiscoveryManagerForType(serviceType)) {
803                             if (serviceType == null) {
804                                 clientInfo.onResolveServiceFailed(clientId,
805                                         NsdManager.FAILURE_INTERNAL_ERROR);
806                                 break;
807                             }
808                             final String resolveServiceType = serviceType + ".local";
809 
810                             maybeStartMonitoringSockets();
811                             final MdnsListener listener =
812                                     new ResolutionListener(clientId, id, info, resolveServiceType);
813                             final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
814                                     .setNetwork(info.getNetwork())
815                                     .setIsPassiveMode(true)
816                                     .setResolveInstanceName(info.getServiceName())
817                                     .setRemoveExpiredService(true)
818                                     .build();
819                             mMdnsDiscoveryManager.registerListener(
820                                     resolveServiceType, listener, options);
821                             storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
822                             clientInfo.log("Register a ResolutionListener " + id
823                                     + " for service type:" + resolveServiceType);
824                         } else {
825                             if (clientInfo.mResolvedService != null) {
826                                 clientInfo.onResolveServiceFailed(
827                                         clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
828                                 break;
829                             }
830 
831                             maybeStartDaemon();
832                             if (resolveService(id, info)) {
833                                 clientInfo.mResolvedService = new NsdServiceInfo();
834                                 storeLegacyRequestMap(clientId, id, clientInfo, msg.what);
835                             } else {
836                                 clientInfo.onResolveServiceFailed(
837                                         clientId, NsdManager.FAILURE_INTERNAL_ERROR);
838                             }
839                         }
840                         break;
841                     }
842                     case NsdManager.STOP_RESOLUTION: {
843                         if (DBG) Log.d(TAG, "Stop service resolution");
844                         args = (ListenerArgs) msg.obj;
845                         clientInfo = mClients.get(args.connector);
846                         // If the binder death notification for a INsdManagerCallback was received
847                         // before any calls are received by NsdService, the clientInfo would be
848                         // cleared and cause NPE. Add a null check here to prevent this corner case.
849                         if (clientInfo == null) {
850                             Log.e(TAG, "Unknown connector in stop resolution");
851                             break;
852                         }
853 
854                         final ClientRequest request = clientInfo.mClientRequests.get(clientId);
855                         if (request == null) {
856                             Log.e(TAG, "Unknown client request in STOP_RESOLUTION");
857                             break;
858                         }
859                         id = request.mGlobalId;
860                         // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
861                         // point, so this needs to check the type of the original request to
862                         // unregister instead of looking at the flag value.
863                         if (request instanceof DiscoveryManagerRequest) {
864                             stopDiscoveryManagerRequest(request, clientId, id, clientInfo);
865                             clientInfo.onStopResolutionSucceeded(clientId);
866                             clientInfo.log("Unregister the ResolutionListener " + id);
867                         } else {
868                             removeRequestMap(clientId, id, clientInfo);
869                             if (stopResolveService(id)) {
870                                 clientInfo.onStopResolutionSucceeded(clientId);
871                             } else {
872                                 clientInfo.onStopResolutionFailed(
873                                         clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
874                             }
875                             clientInfo.mResolvedService = null;
876                         }
877                         break;
878                     }
879                     case NsdManager.REGISTER_SERVICE_CALLBACK: {
880                         if (DBG) Log.d(TAG, "Register a service callback");
881                         args = (ListenerArgs) msg.obj;
882                         clientInfo = mClients.get(args.connector);
883                         // If the binder death notification for a INsdManagerCallback was received
884                         // before any calls are received by NsdService, the clientInfo would be
885                         // cleared and cause NPE. Add a null check here to prevent this corner case.
886                         if (clientInfo == null) {
887                             Log.e(TAG, "Unknown connector in callback registration");
888                             break;
889                         }
890 
891                         final NsdServiceInfo info = args.serviceInfo;
892                         id = getUniqueId();
893                         final Pair<String, String> typeAndSubtype =
894                                 parseTypeAndSubtype(info.getServiceType());
895                         final String serviceType = typeAndSubtype == null
896                                 ? null : typeAndSubtype.first;
897                         if (serviceType == null) {
898                             clientInfo.onServiceInfoCallbackRegistrationFailed(clientId,
899                                     NsdManager.FAILURE_BAD_PARAMETERS);
900                             break;
901                         }
902                         final String resolveServiceType = serviceType + ".local";
903 
904                         maybeStartMonitoringSockets();
905                         final MdnsListener listener =
906                                 new ServiceInfoListener(clientId, id, info, resolveServiceType);
907                         final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
908                                 .setNetwork(info.getNetwork())
909                                 .setIsPassiveMode(true)
910                                 .setResolveInstanceName(info.getServiceName())
911                                 .setRemoveExpiredService(true)
912                                 .build();
913                         mMdnsDiscoveryManager.registerListener(
914                                 resolveServiceType, listener, options);
915                         storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
916                         clientInfo.log("Register a ServiceInfoListener " + id
917                                 + " for service type:" + resolveServiceType);
918                         break;
919                     }
920                     case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
921                         if (DBG) Log.d(TAG, "Unregister a service callback");
922                         args = (ListenerArgs) msg.obj;
923                         clientInfo = mClients.get(args.connector);
924                         // If the binder death notification for a INsdManagerCallback was received
925                         // before any calls are received by NsdService, the clientInfo would be
926                         // cleared and cause NPE. Add a null check here to prevent this corner case.
927                         if (clientInfo == null) {
928                             Log.e(TAG, "Unknown connector in callback unregistration");
929                             break;
930                         }
931 
932                         final ClientRequest request = clientInfo.mClientRequests.get(clientId);
933                         if (request == null) {
934                             Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK");
935                             break;
936                         }
937                         id = request.mGlobalId;
938                         if (request instanceof DiscoveryManagerRequest) {
939                             stopDiscoveryManagerRequest(request, clientId, id, clientInfo);
940                             clientInfo.onServiceInfoCallbackUnregistered(clientId);
941                             clientInfo.log("Unregister the ServiceInfoListener " + id);
942                         } else {
943                             loge("Unregister failed with non-DiscoveryManagerRequest.");
944                         }
945                         break;
946                     }
947                     case MDNS_SERVICE_EVENT:
948                         if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
949                             return NOT_HANDLED;
950                         }
951                         break;
952                     case MDNS_DISCOVERY_MANAGER_EVENT:
953                         if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) {
954                             return NOT_HANDLED;
955                         }
956                         break;
957                     default:
958                         return NOT_HANDLED;
959                 }
960                 return HANDLED;
961             }
962 
handleMDnsServiceEvent(int code, int id, Object obj)963             private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
964                 NsdServiceInfo servInfo;
965                 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
966                 if (clientInfo == null) {
967                     Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
968                     return false;
969                 }
970 
971                 /* This goes in response as msg.arg2 */
972                 int clientId = clientInfo.getClientId(id);
973                 if (clientId < 0) {
974                     // This can happen because of race conditions. For example,
975                     // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
976                     // and we may get in this situation.
977                     Log.d(TAG, String.format("%d for listener id %d that is no longer active",
978                             code, id));
979                     return false;
980                 }
981                 if (DBG) {
982                     Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
983                 }
984                 switch (code) {
985                     case IMDnsEventListener.SERVICE_FOUND: {
986                         final DiscoveryInfo info = (DiscoveryInfo) obj;
987                         final String name = info.serviceName;
988                         final String type = info.registrationType;
989                         servInfo = new NsdServiceInfo(name, type);
990                         final int foundNetId = info.netId;
991                         if (foundNetId == 0L) {
992                             // Ignore services that do not have a Network: they are not usable
993                             // by apps, as they would need privileged permissions to use
994                             // interfaces that do not have an associated Network.
995                             break;
996                         }
997                         if (foundNetId == INetd.DUMMY_NET_ID) {
998                             // Ignore services on the dummy0 interface: they are only seen when
999                             // discovering locally advertised services, and are not reachable
1000                             // through that interface.
1001                             break;
1002                         }
1003                         setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
1004                         clientInfo.onServiceFound(clientId, servInfo);
1005                         break;
1006                     }
1007                     case IMDnsEventListener.SERVICE_LOST: {
1008                         final DiscoveryInfo info = (DiscoveryInfo) obj;
1009                         final String name = info.serviceName;
1010                         final String type = info.registrationType;
1011                         final int lostNetId = info.netId;
1012                         servInfo = new NsdServiceInfo(name, type);
1013                         // The network could be set to null (netId 0) if it was torn down when the
1014                         // service is lost
1015                         // TODO: avoid returning null in that case, possibly by remembering
1016                         // found services on the same interface index and their network at the time
1017                         setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
1018                         clientInfo.onServiceLost(clientId, servInfo);
1019                         break;
1020                     }
1021                     case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
1022                         clientInfo.onDiscoverServicesFailed(
1023                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
1024                         break;
1025                     case IMDnsEventListener.SERVICE_REGISTERED: {
1026                         final RegistrationInfo info = (RegistrationInfo) obj;
1027                         final String name = info.serviceName;
1028                         servInfo = new NsdServiceInfo(name, null /* serviceType */);
1029                         clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
1030                         break;
1031                     }
1032                     case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
1033                         clientInfo.onRegisterServiceFailed(
1034                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
1035                         break;
1036                     case IMDnsEventListener.SERVICE_RESOLVED: {
1037                         final ResolutionInfo info = (ResolutionInfo) obj;
1038                         int index = 0;
1039                         final String fullName = info.serviceFullName;
1040                         while (index < fullName.length() && fullName.charAt(index) != '.') {
1041                             if (fullName.charAt(index) == '\\') {
1042                                 ++index;
1043                             }
1044                             ++index;
1045                         }
1046                         if (index >= fullName.length()) {
1047                             Log.e(TAG, "Invalid service found " + fullName);
1048                             break;
1049                         }
1050 
1051                         String name = unescape(fullName.substring(0, index));
1052                         String rest = fullName.substring(index);
1053                         String type = rest.replace(".local.", "");
1054 
1055                         final NsdServiceInfo serviceInfo = clientInfo.mResolvedService;
1056                         serviceInfo.setServiceName(name);
1057                         serviceInfo.setServiceType(type);
1058                         serviceInfo.setPort(info.port);
1059                         serviceInfo.setTxtRecords(info.txtRecord);
1060                         // Network will be added after SERVICE_GET_ADDR_SUCCESS
1061 
1062                         stopResolveService(id);
1063                         removeRequestMap(clientId, id, clientInfo);
1064 
1065                         final int id2 = getUniqueId();
1066                         if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
1067                             storeLegacyRequestMap(clientId, id2, clientInfo,
1068                                     NsdManager.RESOLVE_SERVICE);
1069                         } else {
1070                             clientInfo.onResolveServiceFailed(
1071                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
1072                             clientInfo.mResolvedService = null;
1073                         }
1074                         break;
1075                     }
1076                     case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
1077                         /* NNN resolveId errorCode */
1078                         stopResolveService(id);
1079                         removeRequestMap(clientId, id, clientInfo);
1080                         clientInfo.onResolveServiceFailed(
1081                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
1082                         clientInfo.mResolvedService = null;
1083                         break;
1084                     case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
1085                         /* NNN resolveId errorCode */
1086                         stopGetAddrInfo(id);
1087                         removeRequestMap(clientId, id, clientInfo);
1088                         clientInfo.onResolveServiceFailed(
1089                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
1090                         clientInfo.mResolvedService = null;
1091                         break;
1092                     case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
1093                         /* NNN resolveId hostname ttl addr interfaceIdx netId */
1094                         final GetAddressInfo info = (GetAddressInfo) obj;
1095                         final String address = info.address;
1096                         final int netId = info.netId;
1097                         InetAddress serviceHost = null;
1098                         try {
1099                             serviceHost = InetAddress.getByName(address);
1100                         } catch (UnknownHostException e) {
1101                             Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
1102                         }
1103 
1104                         // If the resolved service is on an interface without a network, consider it
1105                         // as a failure: it would not be usable by apps as they would need
1106                         // privileged permissions.
1107                         if (netId != NETID_UNSET && serviceHost != null) {
1108                             clientInfo.mResolvedService.setHost(serviceHost);
1109                             setServiceNetworkForCallback(clientInfo.mResolvedService,
1110                                     netId, info.interfaceIdx);
1111                             clientInfo.onResolveServiceSucceeded(
1112                                     clientId, clientInfo.mResolvedService);
1113                         } else {
1114                             clientInfo.onResolveServiceFailed(
1115                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
1116                         }
1117                         stopGetAddrInfo(id);
1118                         removeRequestMap(clientId, id, clientInfo);
1119                         clientInfo.mResolvedService = null;
1120                         break;
1121                     }
1122                     default:
1123                         return false;
1124                 }
1125                 return true;
1126             }
1127 
1128             @Nullable
buildNsdServiceInfoFromMdnsEvent( final MdnsEvent event, int code)1129             private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(
1130                     final MdnsEvent event, int code) {
1131                 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1132                 final String[] typeArray = serviceInfo.getServiceType();
1133                 final String joinedType;
1134                 if (typeArray.length == 0
1135                         || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) {
1136                     Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: "
1137                             + Arrays.toString(typeArray));
1138                     return null;
1139                 } else {
1140                     joinedType = TextUtils.join(".",
1141                             Arrays.copyOfRange(typeArray, 0, typeArray.length - 1));
1142                 }
1143                 final String serviceType;
1144                 switch (code) {
1145                     case NsdManager.SERVICE_FOUND:
1146                     case NsdManager.SERVICE_LOST:
1147                         // For consistency with historical behavior, discovered service types have
1148                         // a dot at the end.
1149                         serviceType = joinedType + ".";
1150                         break;
1151                     case RESOLVE_SERVICE_SUCCEEDED:
1152                         // For consistency with historical behavior, resolved service types have
1153                         // a dot at the beginning.
1154                         serviceType = "." + joinedType;
1155                         break;
1156                     default:
1157                         serviceType = joinedType;
1158                         break;
1159                 }
1160                 final String serviceName = serviceInfo.getServiceInstanceName();
1161                 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
1162                 final Network network = serviceInfo.getNetwork();
1163                 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a
1164                 // network for Tethering interface. In other words, the network == null means the
1165                 // network has netId = INetd.LOCAL_NET_ID.
1166                 setServiceNetworkForCallback(
1167                         servInfo,
1168                         network == null ? INetd.LOCAL_NET_ID : network.netId,
1169                         serviceInfo.getInterfaceIndex());
1170                 return servInfo;
1171             }
1172 
handleMdnsDiscoveryManagerEvent( int transactionId, int code, Object obj)1173             private boolean handleMdnsDiscoveryManagerEvent(
1174                     int transactionId, int code, Object obj) {
1175                 final ClientInfo clientInfo = mIdToClientInfoMap.get(transactionId);
1176                 if (clientInfo == null) {
1177                     Log.e(TAG, String.format(
1178                             "id %d for %d has no client mapping", transactionId, code));
1179                     return false;
1180                 }
1181 
1182                 final MdnsEvent event = (MdnsEvent) obj;
1183                 final int clientId = event.mClientId;
1184                 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
1185                 // Errors are already logged if null
1186                 if (info == null) return false;
1187                 if (DBG) {
1188                     Log.d(TAG, String.format("MdnsDiscoveryManager event code=%s transactionId=%d",
1189                             NsdManager.nameOf(code), transactionId));
1190                 }
1191                 switch (code) {
1192                     case NsdManager.SERVICE_FOUND:
1193                         clientInfo.onServiceFound(clientId, info);
1194                         break;
1195                     case NsdManager.SERVICE_LOST:
1196                         clientInfo.onServiceLost(clientId, info);
1197                         break;
1198                     case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
1199                         final ClientRequest request = clientInfo.mClientRequests.get(clientId);
1200                         if (request == null) {
1201                             Log.e(TAG, "Unknown client request in RESOLVE_SERVICE_SUCCEEDED");
1202                             break;
1203                         }
1204                         final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1205                         info.setPort(serviceInfo.getPort());
1206 
1207                         Map<String, String> attrs = serviceInfo.getAttributes();
1208                         for (Map.Entry<String, String> kv : attrs.entrySet()) {
1209                             final String key = kv.getKey();
1210                             try {
1211                                 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1212                             } catch (IllegalArgumentException e) {
1213                                 Log.e(TAG, "Invalid attribute", e);
1214                             }
1215                         }
1216                         final List<InetAddress> addresses = getInetAddresses(serviceInfo);
1217                         if (addresses.size() != 0) {
1218                             info.setHostAddresses(addresses);
1219                             clientInfo.onResolveServiceSucceeded(clientId, info);
1220                         } else {
1221                             // No address. Notify resolution failure.
1222                             clientInfo.onResolveServiceFailed(
1223                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
1224                         }
1225 
1226                         // Unregister the listener immediately like IMDnsEventListener design
1227                         if (!(request instanceof DiscoveryManagerRequest)) {
1228                             Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event");
1229                             break;
1230                         }
1231                         stopDiscoveryManagerRequest(request, clientId, transactionId, clientInfo);
1232                         break;
1233                     }
1234                     case NsdManager.SERVICE_UPDATED: {
1235                         final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1236                         info.setPort(serviceInfo.getPort());
1237 
1238                         Map<String, String> attrs = serviceInfo.getAttributes();
1239                         for (Map.Entry<String, String> kv : attrs.entrySet()) {
1240                             final String key = kv.getKey();
1241                             try {
1242                                 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1243                             } catch (IllegalArgumentException e) {
1244                                 Log.e(TAG, "Invalid attribute", e);
1245                             }
1246                         }
1247 
1248                         final List<InetAddress> addresses = getInetAddresses(serviceInfo);
1249                         info.setHostAddresses(addresses);
1250                         clientInfo.onServiceUpdated(clientId, info);
1251                         break;
1252                     }
1253                     case NsdManager.SERVICE_UPDATED_LOST:
1254                         clientInfo.onServiceUpdatedLost(clientId);
1255                         break;
1256                     default:
1257                         return false;
1258                 }
1259                 return true;
1260             }
1261        }
1262     }
1263 
1264     @NonNull
getInetAddresses(@onNull MdnsServiceInfo serviceInfo)1265     private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) {
1266         final List<String> v4Addrs = serviceInfo.getIpv4Addresses();
1267         final List<String> v6Addrs = serviceInfo.getIpv6Addresses();
1268         final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size());
1269         for (String ipv4Address : v4Addrs) {
1270             try {
1271                 addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
1272             } catch (IllegalArgumentException e) {
1273                 Log.wtf(TAG, "Invalid ipv4 address", e);
1274             }
1275         }
1276         for (String ipv6Address : v6Addrs) {
1277             try {
1278                 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress(
1279                         ipv6Address);
1280                 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex()));
1281             } catch (IllegalArgumentException e) {
1282                 Log.wtf(TAG, "Invalid ipv6 address", e);
1283             }
1284         }
1285         return addresses;
1286     }
1287 
setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx)1288     private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
1289         switch (netId) {
1290             case NETID_UNSET:
1291                 info.setNetwork(null);
1292                 break;
1293             case INetd.LOCAL_NET_ID:
1294                 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
1295                 // visible / usable for apps, so do not return it. Store the interface
1296                 // index instead, so at least if the client tries to resolve the service
1297                 // with that NsdServiceInfo, it will be done on the same interface.
1298                 // If they recreate the NsdServiceInfo themselves, resolution would be
1299                 // done on all interfaces as before T, which should also work.
1300                 info.setNetwork(null);
1301                 info.setInterfaceIndex(ifaceIdx);
1302                 break;
1303             default:
1304                 info.setNetwork(new Network(netId));
1305         }
1306     }
1307 
1308     // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
1309     // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
1310     // unescape for getting right service address. See "Notes on DNS Name Escaping" on
1311     // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
unescape(String s)1312     private String unescape(String s) {
1313         StringBuilder sb = new StringBuilder(s.length());
1314         for (int i = 0; i < s.length(); ++i) {
1315             char c = s.charAt(i);
1316             if (c == '\\') {
1317                 if (++i >= s.length()) {
1318                     Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1319                     break;
1320                 }
1321                 c = s.charAt(i);
1322                 if (c != '.' && c != '\\') {
1323                     if (i + 2 >= s.length()) {
1324                         Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1325                         break;
1326                     }
1327                     c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
1328                             + (s.charAt(i + 2) - '0'));
1329                     i += 2;
1330                 }
1331             }
1332             sb.append(c);
1333         }
1334         return sb.toString();
1335     }
1336 
1337     /**
1338      * Check the given service type is valid and construct it to a service type
1339      * which can use for discovery / resolution service.
1340      *
1341      * <p>The valid service type should be 2 labels, or 3 labels if the query is for a
1342      * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
1343      * underscore; they are alphanumerical characters or dashes or underscore, except the
1344      * last one that is just alphanumerical. The last label must be _tcp or _udp.
1345      *
1346      * <p>The subtype may also be specified with a comma after the service type, for example
1347      * _type._tcp,_subtype.
1348      *
1349      * @param serviceType the request service type for discovery / resolution service
1350      * @return constructed service type or null if the given service type is invalid.
1351      */
1352     @Nullable
parseTypeAndSubtype(String serviceType)1353     public static Pair<String, String> parseTypeAndSubtype(String serviceType) {
1354         if (TextUtils.isEmpty(serviceType)) return null;
1355 
1356         final String typeOrSubtypePattern = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]";
1357         final Pattern serviceTypePattern = Pattern.compile(
1358                 // Optional leading subtype (_subtype._type._tcp)
1359                 // (?: xxx) is a non-capturing parenthesis, don't capture the dot
1360                 "^(?:(" + typeOrSubtypePattern + ")\\.)?"
1361                         // Actual type (_type._tcp.local)
1362                         + "(" + typeOrSubtypePattern + "\\._(?:tcp|udp))"
1363                         // Drop '.' at the end of service type that is compatible with old backend.
1364                         // e.g. allow "_type._tcp.local."
1365                         + "\\.?"
1366                         // Optional subtype after comma, for "_type._tcp,_subtype" format
1367                         + "(?:,(" + typeOrSubtypePattern + "))?"
1368                         + "$");
1369         final Matcher matcher = serviceTypePattern.matcher(serviceType);
1370         if (!matcher.matches()) return null;
1371         // Use the subtype either at the beginning or after the comma
1372         final String subtype = matcher.group(1) != null ? matcher.group(1) : matcher.group(3);
1373         return new Pair<>(matcher.group(2), subtype);
1374     }
1375 
1376     @VisibleForTesting
NsdService(Context ctx, Handler handler, long cleanupDelayMs)1377     NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
1378         this(ctx, handler, cleanupDelayMs, new Dependencies());
1379     }
1380 
1381     @VisibleForTesting
NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps)1382     NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) {
1383         mCleanupDelayMs = cleanupDelayMs;
1384         mContext = ctx;
1385         mNsdStateMachine = new NsdStateMachine(TAG, handler);
1386         mNsdStateMachine.start();
1387         mMDnsManager = ctx.getSystemService(MDnsManager.class);
1388         mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
1389         mDeps = deps;
1390 
1391         mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
1392                 LOGGER.forSubComponent("MdnsSocketProvider"));
1393         // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
1394         // address events are received.
1395         handler.post(mMdnsSocketProvider::startNetLinkMonitor);
1396         mMdnsSocketClient =
1397                 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
1398         mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
1399                 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"));
1400         handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
1401         mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
1402                 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"));
1403     }
1404 
1405     /**
1406      * Dependencies of NsdService, for injection in tests.
1407      */
1408     @VisibleForTesting
1409     public static class Dependencies {
1410         /**
1411          * Check whether the MdnsDiscoveryManager feature is enabled.
1412          *
1413          * @param context The global context information about an app environment.
1414          * @return true if the MdnsDiscoveryManager feature is enabled.
1415          */
isMdnsDiscoveryManagerEnabled(Context context)1416         public boolean isMdnsDiscoveryManagerEnabled(Context context) {
1417             return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
1418                     MDNS_DISCOVERY_MANAGER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME,
1419                     false /* defaultEnabled */);
1420         }
1421 
1422         /**
1423          * Check whether the MdnsAdvertiser feature is enabled.
1424          *
1425          * @param context The global context information about an app environment.
1426          * @return true if the MdnsAdvertiser feature is enabled.
1427          */
isMdnsAdvertiserEnabled(Context context)1428         public boolean isMdnsAdvertiserEnabled(Context context) {
1429             return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
1430                     MDNS_ADVERTISER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME,
1431                     false /* defaultEnabled */);
1432         }
1433 
1434         /**
1435          * Get the type allowlist flag value.
1436          * @see #MDNS_TYPE_ALLOWLIST_FLAGS
1437          */
1438         @Nullable
getTypeAllowlistFlags()1439         public String getTypeAllowlistFlags() {
1440             return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING,
1441                     MDNS_TYPE_ALLOWLIST_FLAGS, null);
1442         }
1443 
1444         /**
1445          * @see DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean)
1446          */
isFeatureEnabled(Context context, String feature)1447         public boolean isFeatureEnabled(Context context, String feature) {
1448             return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
1449                     feature, DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
1450         }
1451 
1452         /**
1453          * @see MdnsDiscoveryManager
1454          */
makeMdnsDiscoveryManager( @onNull ExecutorProvider executorProvider, @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog)1455         public MdnsDiscoveryManager makeMdnsDiscoveryManager(
1456                 @NonNull ExecutorProvider executorProvider,
1457                 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog) {
1458             return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog);
1459         }
1460 
1461         /**
1462          * @see MdnsAdvertiser
1463          */
makeMdnsAdvertiser( @onNull Looper looper, @NonNull MdnsSocketProvider socketProvider, @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog)1464         public MdnsAdvertiser makeMdnsAdvertiser(
1465                 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
1466                 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog) {
1467             return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog);
1468         }
1469 
1470         /**
1471          * @see MdnsSocketProvider
1472          */
makeMdnsSocketProvider(@onNull Context context, @NonNull Looper looper, @NonNull SharedLog sharedLog)1473         public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
1474                 @NonNull Looper looper, @NonNull SharedLog sharedLog) {
1475             return new MdnsSocketProvider(context, looper, sharedLog);
1476         }
1477     }
1478 
1479     /**
1480      * Return whether a type is allowlisted to use the Java backend.
1481      * @param type The service type
1482      * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or
1483      *                   {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}.
1484      */
isTypeAllowlistedForJavaBackend(@ullable String type, @NonNull String flagPrefix)1485     private boolean isTypeAllowlistedForJavaBackend(@Nullable String type,
1486             @NonNull String flagPrefix) {
1487         if (type == null) return false;
1488         final String typesConfig = mDeps.getTypeAllowlistFlags();
1489         if (TextUtils.isEmpty(typesConfig)) return false;
1490 
1491         final String mappingPrefix = type + ":";
1492         String mappedFlag = null;
1493         for (String mapping : TextUtils.split(typesConfig, ",")) {
1494             if (mapping.startsWith(mappingPrefix)) {
1495                 mappedFlag = mapping.substring(mappingPrefix.length());
1496                 break;
1497             }
1498         }
1499 
1500         if (mappedFlag == null) return false;
1501 
1502         return mDeps.isFeatureEnabled(mContext,
1503                 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX);
1504     }
1505 
useDiscoveryManagerForType(@ullable String type)1506     private boolean useDiscoveryManagerForType(@Nullable String type) {
1507         return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX);
1508     }
1509 
useAdvertiserForType(@ullable String type)1510     private boolean useAdvertiserForType(@Nullable String type) {
1511         return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX);
1512     }
1513 
create(Context context)1514     public static NsdService create(Context context) {
1515         HandlerThread thread = new HandlerThread(TAG);
1516         thread.start();
1517         Handler handler = new Handler(thread.getLooper());
1518         NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
1519         return service;
1520     }
1521 
1522     private static class MDnsEventCallback extends IMDnsEventListener.Stub {
1523         private final StateMachine mStateMachine;
1524 
MDnsEventCallback(StateMachine sm)1525         MDnsEventCallback(StateMachine sm) {
1526             mStateMachine = sm;
1527         }
1528 
1529         @Override
onServiceRegistrationStatus(final RegistrationInfo status)1530         public void onServiceRegistrationStatus(final RegistrationInfo status) {
1531             mStateMachine.sendMessage(
1532                     MDNS_SERVICE_EVENT, status.result, status.id, status);
1533         }
1534 
1535         @Override
onServiceDiscoveryStatus(final DiscoveryInfo status)1536         public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
1537             mStateMachine.sendMessage(
1538                     MDNS_SERVICE_EVENT, status.result, status.id, status);
1539         }
1540 
1541         @Override
onServiceResolutionStatus(final ResolutionInfo status)1542         public void onServiceResolutionStatus(final ResolutionInfo status) {
1543             mStateMachine.sendMessage(
1544                     MDNS_SERVICE_EVENT, status.result, status.id, status);
1545         }
1546 
1547         @Override
onGettingServiceAddressStatus(final GetAddressInfo status)1548         public void onGettingServiceAddressStatus(final GetAddressInfo status) {
1549             mStateMachine.sendMessage(
1550                     MDNS_SERVICE_EVENT, status.result, status.id, status);
1551         }
1552 
1553         @Override
getInterfaceVersion()1554         public int getInterfaceVersion() throws RemoteException {
1555             return this.VERSION;
1556         }
1557 
1558         @Override
getInterfaceHash()1559         public String getInterfaceHash() throws RemoteException {
1560             return this.HASH;
1561         }
1562     }
1563 
1564     private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
1565         @Override
onRegisterServiceSucceeded(int serviceId, NsdServiceInfo registeredInfo)1566         public void onRegisterServiceSucceeded(int serviceId, NsdServiceInfo registeredInfo) {
1567             final ClientInfo clientInfo = getClientInfoOrLog(serviceId);
1568             if (clientInfo == null) return;
1569 
1570             final int clientId = getClientIdOrLog(clientInfo, serviceId);
1571             if (clientId < 0) return;
1572 
1573             // onRegisterServiceSucceeded only has the service name in its info. This aligns with
1574             // historical behavior.
1575             final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
1576             clientInfo.onRegisterServiceSucceeded(clientId, cbInfo);
1577         }
1578 
1579         @Override
onRegisterServiceFailed(int serviceId, int errorCode)1580         public void onRegisterServiceFailed(int serviceId, int errorCode) {
1581             final ClientInfo clientInfo = getClientInfoOrLog(serviceId);
1582             if (clientInfo == null) return;
1583 
1584             final int clientId = getClientIdOrLog(clientInfo, serviceId);
1585             if (clientId < 0) return;
1586 
1587             clientInfo.onRegisterServiceFailed(clientId, errorCode);
1588         }
1589 
getClientInfoOrLog(int serviceId)1590         private ClientInfo getClientInfoOrLog(int serviceId) {
1591             final ClientInfo clientInfo = mIdToClientInfoMap.get(serviceId);
1592             if (clientInfo == null) {
1593                 Log.e(TAG, String.format("Callback for service %d has no client", serviceId));
1594             }
1595             return clientInfo;
1596         }
1597 
getClientIdOrLog(@onNull ClientInfo info, int serviceId)1598         private int getClientIdOrLog(@NonNull ClientInfo info, int serviceId) {
1599             final int clientId = info.getClientId(serviceId);
1600             if (clientId < 0) {
1601                 Log.e(TAG, String.format("Client ID not found for service %d", serviceId));
1602             }
1603             return clientId;
1604         }
1605     }
1606 
1607     private static class ConnectorArgs {
1608         @NonNull public final NsdServiceConnector connector;
1609         @NonNull public final INsdManagerCallback callback;
1610         public final boolean useJavaBackend;
1611         public final int uid;
1612 
ConnectorArgs(@onNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback, boolean useJavaBackend, int uid)1613         ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
1614                 boolean useJavaBackend, int uid) {
1615             this.connector = connector;
1616             this.callback = callback;
1617             this.useJavaBackend = useJavaBackend;
1618             this.uid = uid;
1619         }
1620     }
1621 
1622     @Override
connect(INsdManagerCallback cb, boolean useJavaBackend)1623     public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
1624         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
1625         if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
1626         final INsdServiceConnector connector = new NsdServiceConnector();
1627         mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
1628                 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend,
1629                         Binder.getCallingUid())));
1630         return connector;
1631     }
1632 
1633     private static class ListenerArgs {
1634         public final NsdServiceConnector connector;
1635         public final NsdServiceInfo serviceInfo;
ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo)1636         ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
1637             this.connector = connector;
1638             this.serviceInfo = serviceInfo;
1639         }
1640     }
1641 
1642     private class NsdServiceConnector extends INsdServiceConnector.Stub
1643             implements IBinder.DeathRecipient  {
1644         @Override
registerService(int listenerKey, NsdServiceInfo serviceInfo)1645         public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
1646             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1647                     NsdManager.REGISTER_SERVICE, 0, listenerKey,
1648                     new ListenerArgs(this, serviceInfo)));
1649         }
1650 
1651         @Override
unregisterService(int listenerKey)1652         public void unregisterService(int listenerKey) {
1653             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1654                     NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
1655                     new ListenerArgs(this, null)));
1656         }
1657 
1658         @Override
discoverServices(int listenerKey, NsdServiceInfo serviceInfo)1659         public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
1660             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1661                     NsdManager.DISCOVER_SERVICES, 0, listenerKey,
1662                     new ListenerArgs(this, serviceInfo)));
1663         }
1664 
1665         @Override
stopDiscovery(int listenerKey)1666         public void stopDiscovery(int listenerKey) {
1667             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1668                     NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
1669         }
1670 
1671         @Override
resolveService(int listenerKey, NsdServiceInfo serviceInfo)1672         public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
1673             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1674                     NsdManager.RESOLVE_SERVICE, 0, listenerKey,
1675                     new ListenerArgs(this, serviceInfo)));
1676         }
1677 
1678         @Override
stopResolution(int listenerKey)1679         public void stopResolution(int listenerKey) {
1680             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1681                     NsdManager.STOP_RESOLUTION, 0, listenerKey, new ListenerArgs(this, null)));
1682         }
1683 
1684         @Override
registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo)1685         public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
1686             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1687                     NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
1688                     new ListenerArgs(this, serviceInfo)));
1689         }
1690 
1691         @Override
unregisterServiceInfoCallback(int listenerKey)1692         public void unregisterServiceInfoCallback(int listenerKey) {
1693             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1694                     NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
1695                     new ListenerArgs(this, null)));
1696         }
1697 
1698         @Override
startDaemon()1699         public void startDaemon() {
1700             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1701                     NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
1702         }
1703 
1704         @Override
binderDied()1705         public void binderDied() {
1706             mNsdStateMachine.sendMessage(
1707                     mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
1708         }
1709     }
1710 
sendNsdStateChangeBroadcast(boolean isEnabled)1711     private void sendNsdStateChangeBroadcast(boolean isEnabled) {
1712         final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
1713         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1714         int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
1715         intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
1716         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1717     }
1718 
getUniqueId()1719     private int getUniqueId() {
1720         if (++mUniqueId == INVALID_ID) return ++mUniqueId;
1721         return mUniqueId;
1722     }
1723 
registerService(int regId, NsdServiceInfo service)1724     private boolean registerService(int regId, NsdServiceInfo service) {
1725         if (DBG) {
1726             Log.d(TAG, "registerService: " + regId + " " + service);
1727         }
1728         String name = service.getServiceName();
1729         String type = service.getServiceType();
1730         int port = service.getPort();
1731         byte[] textRecord = service.getTxtRecord();
1732         final int registerInterface = getNetworkInterfaceIndex(service);
1733         if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
1734             Log.e(TAG, "Interface to register service on not found");
1735             return false;
1736         }
1737         return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface);
1738     }
1739 
unregisterService(int regId)1740     private boolean unregisterService(int regId) {
1741         return mMDnsManager.stopOperation(regId);
1742     }
1743 
discoverServices(int discoveryId, NsdServiceInfo serviceInfo)1744     private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
1745         final String type = serviceInfo.getServiceType();
1746         final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
1747         if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
1748             Log.e(TAG, "Interface to discover service on not found");
1749             return false;
1750         }
1751         return mMDnsManager.discover(discoveryId, type, discoverInterface);
1752     }
1753 
stopServiceDiscovery(int discoveryId)1754     private boolean stopServiceDiscovery(int discoveryId) {
1755         return mMDnsManager.stopOperation(discoveryId);
1756     }
1757 
resolveService(int resolveId, NsdServiceInfo service)1758     private boolean resolveService(int resolveId, NsdServiceInfo service) {
1759         final String name = service.getServiceName();
1760         final String type = service.getServiceType();
1761         final int resolveInterface = getNetworkInterfaceIndex(service);
1762         if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
1763             Log.e(TAG, "Interface to resolve service on not found");
1764             return false;
1765         }
1766         return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
1767     }
1768 
1769     /**
1770      * Guess the interface to use to resolve or discover a service on a specific network.
1771      *
1772      * This is an imperfect guess, as for example the network may be gone or not yet fully
1773      * registered. This is fine as failing is correct if the network is gone, and a client
1774      * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
1775      * this is to support the legacy mdnsresponder implementation, which historically resolved
1776      * services on an unspecified network.
1777      */
getNetworkInterfaceIndex(NsdServiceInfo serviceInfo)1778     private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
1779         final Network network = serviceInfo.getNetwork();
1780         if (network == null) {
1781             // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
1782             // provided by NsdService from discovery results, and the service was found on an
1783             // interface that has no app-usable Network).
1784             if (serviceInfo.getInterfaceIndex() != 0) {
1785                 return serviceInfo.getInterfaceIndex();
1786             }
1787             return IFACE_IDX_ANY;
1788         }
1789 
1790         final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
1791         if (cm == null) {
1792             Log.wtf(TAG, "No ConnectivityManager for resolveService");
1793             return IFACE_IDX_ANY;
1794         }
1795         final LinkProperties lp = cm.getLinkProperties(network);
1796         if (lp == null) return IFACE_IDX_ANY;
1797 
1798         // Only resolve on non-stacked interfaces
1799         final NetworkInterface iface;
1800         try {
1801             iface = NetworkInterface.getByName(lp.getInterfaceName());
1802         } catch (SocketException e) {
1803             Log.e(TAG, "Error querying interface", e);
1804             return IFACE_IDX_ANY;
1805         }
1806 
1807         if (iface == null) {
1808             Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
1809             return IFACE_IDX_ANY;
1810         }
1811 
1812         return iface.getIndex();
1813     }
1814 
stopResolveService(int resolveId)1815     private boolean stopResolveService(int resolveId) {
1816         return mMDnsManager.stopOperation(resolveId);
1817     }
1818 
getAddrInfo(int resolveId, String hostname, int interfaceIdx)1819     private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
1820         return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
1821     }
1822 
stopGetAddrInfo(int resolveId)1823     private boolean stopGetAddrInfo(int resolveId) {
1824         return mMDnsManager.stopOperation(resolveId);
1825     }
1826 
1827     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)1828     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
1829         if (!PermissionUtils.checkDumpPermission(mContext, TAG, writer)) return;
1830 
1831         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
1832         // Dump state machine logs
1833         mNsdStateMachine.dump(fd, pw, args);
1834 
1835         // Dump service and clients logs
1836         pw.println();
1837         pw.println("Logs:");
1838         pw.increaseIndent();
1839         mServiceLogs.reverseDump(pw);
1840         pw.decreaseIndent();
1841     }
1842 
1843     private abstract static class ClientRequest {
1844         private final int mGlobalId;
1845 
ClientRequest(int globalId)1846         private ClientRequest(int globalId) {
1847             mGlobalId = globalId;
1848         }
1849     }
1850 
1851     private static class LegacyClientRequest extends ClientRequest {
1852         private final int mRequestCode;
1853 
LegacyClientRequest(int globalId, int requestCode)1854         private LegacyClientRequest(int globalId, int requestCode) {
1855             super(globalId);
1856             mRequestCode = requestCode;
1857         }
1858     }
1859 
1860     private static class AdvertiserClientRequest extends ClientRequest {
AdvertiserClientRequest(int globalId)1861         private AdvertiserClientRequest(int globalId) {
1862             super(globalId);
1863         }
1864     }
1865 
1866     private static class DiscoveryManagerRequest extends ClientRequest {
1867         @NonNull
1868         private final MdnsListener mListener;
1869 
DiscoveryManagerRequest(int globalId, @NonNull MdnsListener listener)1870         private DiscoveryManagerRequest(int globalId, @NonNull MdnsListener listener) {
1871             super(globalId);
1872             mListener = listener;
1873         }
1874     }
1875 
1876     /* Information tracked per client */
1877     private class ClientInfo {
1878 
1879         private static final int MAX_LIMIT = 10;
1880         private final INsdManagerCallback mCb;
1881         /* Remembers a resolved service until getaddrinfo completes */
1882         private NsdServiceInfo mResolvedService;
1883 
1884         /* A map from client-side ID (listenerKey) to the request */
1885         private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>();
1886 
1887         // The target SDK of this client < Build.VERSION_CODES.S
1888         private boolean mIsPreSClient = false;
1889         // The flag of using java backend if the client's target SDK >= U
1890         private final boolean mUseJavaBackend;
1891         // Store client logs
1892         private final SharedLog mClientLogs;
1893 
ClientInfo(INsdManagerCallback cb, boolean useJavaBackend, SharedLog sharedLog)1894         private ClientInfo(INsdManagerCallback cb, boolean useJavaBackend, SharedLog sharedLog) {
1895             mCb = cb;
1896             mUseJavaBackend = useJavaBackend;
1897             mClientLogs = sharedLog;
1898             mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
1899         }
1900 
1901         @Override
toString()1902         public String toString() {
1903             StringBuilder sb = new StringBuilder();
1904             sb.append("mResolvedService ").append(mResolvedService).append("\n");
1905             sb.append("mIsLegacy ").append(mIsPreSClient).append("\n");
1906             for (int i = 0; i < mClientRequests.size(); i++) {
1907                 int clientID = mClientRequests.keyAt(i);
1908                 sb.append("clientId ")
1909                         .append(clientID)
1910                         .append(" mDnsId ").append(mClientRequests.valueAt(i).mGlobalId)
1911                         .append(" type ").append(
1912                                 mClientRequests.valueAt(i).getClass().getSimpleName())
1913                         .append("\n");
1914             }
1915             return sb.toString();
1916         }
1917 
isPreSClient()1918         private boolean isPreSClient() {
1919             return mIsPreSClient;
1920         }
1921 
setPreSClient()1922         private void setPreSClient() {
1923             mIsPreSClient = true;
1924         }
1925 
unregisterMdnsListenerFromRequest(ClientRequest request)1926         private void unregisterMdnsListenerFromRequest(ClientRequest request) {
1927             final MdnsListener listener =
1928                     ((DiscoveryManagerRequest) request).mListener;
1929             mMdnsDiscoveryManager.unregisterListener(
1930                     listener.getListenedServiceType(), listener);
1931         }
1932 
1933         // Remove any pending requests from the global map when we get rid of a client,
1934         // and send cancellations to the daemon.
expungeAllRequests()1935         private void expungeAllRequests() {
1936             mClientLogs.log("Client unregistered. expungeAllRequests!");
1937             // TODO: to keep handler responsive, do not clean all requests for that client at once.
1938             for (int i = 0; i < mClientRequests.size(); i++) {
1939                 final int clientId = mClientRequests.keyAt(i);
1940                 final ClientRequest request = mClientRequests.valueAt(i);
1941                 final int globalId = request.mGlobalId;
1942                 mIdToClientInfoMap.remove(globalId);
1943                 if (DBG) {
1944                     Log.d(TAG, "Terminating client-ID " + clientId
1945                             + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
1946                 }
1947 
1948                 if (request instanceof DiscoveryManagerRequest) {
1949                     unregisterMdnsListenerFromRequest(request);
1950                     continue;
1951                 }
1952 
1953                 if (request instanceof AdvertiserClientRequest) {
1954                     mAdvertiser.removeService(globalId);
1955                     continue;
1956                 }
1957 
1958                 if (!(request instanceof LegacyClientRequest)) {
1959                     throw new IllegalStateException("Unknown request type: " + request.getClass());
1960                 }
1961 
1962                 switch (((LegacyClientRequest) request).mRequestCode) {
1963                     case NsdManager.DISCOVER_SERVICES:
1964                         stopServiceDiscovery(globalId);
1965                         break;
1966                     case NsdManager.RESOLVE_SERVICE:
1967                         stopResolveService(globalId);
1968                         break;
1969                     case NsdManager.REGISTER_SERVICE:
1970                         unregisterService(globalId);
1971                         break;
1972                     default:
1973                         break;
1974                 }
1975             }
1976             mClientRequests.clear();
1977         }
1978 
1979         // mClientRequests is a sparse array of listener id -> ClientRequest.  For a given
1980         // mDnsClient id, return the corresponding listener id.  mDnsClient id is also called a
1981         // global id.
getClientId(final int globalId)1982         private int getClientId(final int globalId) {
1983             for (int i = 0; i < mClientRequests.size(); i++) {
1984                 if (mClientRequests.valueAt(i).mGlobalId == globalId) {
1985                     return mClientRequests.keyAt(i);
1986                 }
1987             }
1988             return -1;
1989         }
1990 
log(String message)1991         private void log(String message) {
1992             mClientLogs.log(message);
1993         }
1994 
onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info)1995         void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
1996             try {
1997                 mCb.onDiscoverServicesStarted(listenerKey, info);
1998             } catch (RemoteException e) {
1999                 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
2000             }
2001         }
2002 
onDiscoverServicesFailed(int listenerKey, int error)2003         void onDiscoverServicesFailed(int listenerKey, int error) {
2004             try {
2005                 mCb.onDiscoverServicesFailed(listenerKey, error);
2006             } catch (RemoteException e) {
2007                 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
2008             }
2009         }
2010 
onServiceFound(int listenerKey, NsdServiceInfo info)2011         void onServiceFound(int listenerKey, NsdServiceInfo info) {
2012             try {
2013                 mCb.onServiceFound(listenerKey, info);
2014             } catch (RemoteException e) {
2015                 Log.e(TAG, "Error calling onServiceFound(", e);
2016             }
2017         }
2018 
onServiceLost(int listenerKey, NsdServiceInfo info)2019         void onServiceLost(int listenerKey, NsdServiceInfo info) {
2020             try {
2021                 mCb.onServiceLost(listenerKey, info);
2022             } catch (RemoteException e) {
2023                 Log.e(TAG, "Error calling onServiceLost(", e);
2024             }
2025         }
2026 
onStopDiscoveryFailed(int listenerKey, int error)2027         void onStopDiscoveryFailed(int listenerKey, int error) {
2028             try {
2029                 mCb.onStopDiscoveryFailed(listenerKey, error);
2030             } catch (RemoteException e) {
2031                 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
2032             }
2033         }
2034 
onStopDiscoverySucceeded(int listenerKey)2035         void onStopDiscoverySucceeded(int listenerKey) {
2036             try {
2037                 mCb.onStopDiscoverySucceeded(listenerKey);
2038             } catch (RemoteException e) {
2039                 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
2040             }
2041         }
2042 
onRegisterServiceFailed(int listenerKey, int error)2043         void onRegisterServiceFailed(int listenerKey, int error) {
2044             try {
2045                 mCb.onRegisterServiceFailed(listenerKey, error);
2046             } catch (RemoteException e) {
2047                 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
2048             }
2049         }
2050 
onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info)2051         void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
2052             try {
2053                 mCb.onRegisterServiceSucceeded(listenerKey, info);
2054             } catch (RemoteException e) {
2055                 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
2056             }
2057         }
2058 
onUnregisterServiceFailed(int listenerKey, int error)2059         void onUnregisterServiceFailed(int listenerKey, int error) {
2060             try {
2061                 mCb.onUnregisterServiceFailed(listenerKey, error);
2062             } catch (RemoteException e) {
2063                 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
2064             }
2065         }
2066 
onUnregisterServiceSucceeded(int listenerKey)2067         void onUnregisterServiceSucceeded(int listenerKey) {
2068             try {
2069                 mCb.onUnregisterServiceSucceeded(listenerKey);
2070             } catch (RemoteException e) {
2071                 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
2072             }
2073         }
2074 
onResolveServiceFailed(int listenerKey, int error)2075         void onResolveServiceFailed(int listenerKey, int error) {
2076             try {
2077                 mCb.onResolveServiceFailed(listenerKey, error);
2078             } catch (RemoteException e) {
2079                 Log.e(TAG, "Error calling onResolveServiceFailed", e);
2080             }
2081         }
2082 
onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info)2083         void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
2084             try {
2085                 mCb.onResolveServiceSucceeded(listenerKey, info);
2086             } catch (RemoteException e) {
2087                 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
2088             }
2089         }
2090 
onStopResolutionFailed(int listenerKey, int error)2091         void onStopResolutionFailed(int listenerKey, int error) {
2092             try {
2093                 mCb.onStopResolutionFailed(listenerKey, error);
2094             } catch (RemoteException e) {
2095                 Log.e(TAG, "Error calling onStopResolutionFailed", e);
2096             }
2097         }
2098 
onStopResolutionSucceeded(int listenerKey)2099         void onStopResolutionSucceeded(int listenerKey) {
2100             try {
2101                 mCb.onStopResolutionSucceeded(listenerKey);
2102             } catch (RemoteException e) {
2103                 Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
2104             }
2105         }
2106 
onServiceInfoCallbackRegistrationFailed(int listenerKey, int error)2107         void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
2108             try {
2109                 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
2110             } catch (RemoteException e) {
2111                 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
2112             }
2113         }
2114 
onServiceUpdated(int listenerKey, NsdServiceInfo info)2115         void onServiceUpdated(int listenerKey, NsdServiceInfo info) {
2116             try {
2117                 mCb.onServiceUpdated(listenerKey, info);
2118             } catch (RemoteException e) {
2119                 Log.e(TAG, "Error calling onServiceUpdated", e);
2120             }
2121         }
2122 
onServiceUpdatedLost(int listenerKey)2123         void onServiceUpdatedLost(int listenerKey) {
2124             try {
2125                 mCb.onServiceUpdatedLost(listenerKey);
2126             } catch (RemoteException e) {
2127                 Log.e(TAG, "Error calling onServiceUpdatedLost", e);
2128             }
2129         }
2130 
onServiceInfoCallbackUnregistered(int listenerKey)2131         void onServiceInfoCallbackUnregistered(int listenerKey) {
2132             try {
2133                 mCb.onServiceInfoCallbackUnregistered(listenerKey);
2134             } catch (RemoteException e) {
2135                 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
2136             }
2137         }
2138     }
2139 }
2140