• 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_SERVICE_EVENT;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.net.ConnectivityManager;
26 import android.net.INetd;
27 import android.net.LinkProperties;
28 import android.net.Network;
29 import android.net.mdns.aidl.DiscoveryInfo;
30 import android.net.mdns.aidl.GetAddressInfo;
31 import android.net.mdns.aidl.IMDnsEventListener;
32 import android.net.mdns.aidl.RegistrationInfo;
33 import android.net.mdns.aidl.ResolutionInfo;
34 import android.net.nsd.INsdManager;
35 import android.net.nsd.INsdManagerCallback;
36 import android.net.nsd.INsdServiceConnector;
37 import android.net.nsd.MDnsManager;
38 import android.net.nsd.NsdManager;
39 import android.net.nsd.NsdServiceInfo;
40 import android.os.Handler;
41 import android.os.HandlerThread;
42 import android.os.IBinder;
43 import android.os.Message;
44 import android.os.RemoteException;
45 import android.os.UserHandle;
46 import android.util.Log;
47 import android.util.Pair;
48 import android.util.SparseArray;
49 import android.util.SparseIntArray;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.internal.util.State;
53 import com.android.internal.util.StateMachine;
54 
55 import java.io.FileDescriptor;
56 import java.io.PrintWriter;
57 import java.net.InetAddress;
58 import java.net.NetworkInterface;
59 import java.net.SocketException;
60 import java.net.UnknownHostException;
61 import java.util.HashMap;
62 
63 /**
64  * Network Service Discovery Service handles remote service discovery operation requests by
65  * implementing the INsdManager interface.
66  *
67  * @hide
68  */
69 public class NsdService extends INsdManager.Stub {
70     private static final String TAG = "NsdService";
71     private static final String MDNS_TAG = "mDnsConnector";
72 
73     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
74     private static final long CLEANUP_DELAY_MS = 10000;
75     private static final int IFACE_IDX_ANY = 0;
76 
77     private final Context mContext;
78     private final NsdStateMachine mNsdStateMachine;
79     private final MDnsManager mMDnsManager;
80     private final MDnsEventCallback mMDnsEventCallback;
81     // WARNING : Accessing this value in any thread is not safe, it must only be changed in the
82     // state machine thread. If change this outside state machine, it will need to introduce
83     // synchronization.
84     private boolean mIsDaemonStarted = false;
85 
86     /**
87      * Clients receiving asynchronous messages
88      */
89     private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
90 
91     /* A map from unique id to client info */
92     private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
93 
94     private final long mCleanupDelayMs;
95 
96     private static final int INVALID_ID = 0;
97     private int mUniqueId = 1;
98     // The count of the connected legacy clients.
99     private int mLegacyClientCount = 0;
100 
101     private class NsdStateMachine extends StateMachine {
102 
103         private final DefaultState mDefaultState = new DefaultState();
104         private final DisabledState mDisabledState = new DisabledState();
105         private final EnabledState mEnabledState = new EnabledState();
106 
107         @Override
getWhatToString(int what)108         protected String getWhatToString(int what) {
109             return NsdManager.nameOf(what);
110         }
111 
maybeStartDaemon()112         private void maybeStartDaemon() {
113             if (mIsDaemonStarted) {
114                 if (DBG) Log.d(TAG, "Daemon is already started.");
115                 return;
116             }
117             mMDnsManager.registerEventListener(mMDnsEventCallback);
118             mMDnsManager.startDaemon();
119             mIsDaemonStarted = true;
120             maybeScheduleStop();
121         }
122 
maybeStopDaemon()123         private void maybeStopDaemon() {
124             if (!mIsDaemonStarted) {
125                 if (DBG) Log.d(TAG, "Daemon has not been started.");
126                 return;
127             }
128             mMDnsManager.unregisterEventListener(mMDnsEventCallback);
129             mMDnsManager.stopDaemon();
130             mIsDaemonStarted = false;
131         }
132 
isAnyRequestActive()133         private boolean isAnyRequestActive() {
134             return mIdToClientInfoMap.size() != 0;
135         }
136 
scheduleStop()137         private void scheduleStop() {
138             sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
139         }
maybeScheduleStop()140         private void maybeScheduleStop() {
141             // The native daemon should stay alive and can't be cleanup
142             // if any legacy client connected.
143             if (!isAnyRequestActive() && mLegacyClientCount == 0) {
144                 scheduleStop();
145             }
146         }
147 
cancelStop()148         private void cancelStop() {
149             this.removeMessages(NsdManager.DAEMON_CLEANUP);
150         }
151 
NsdStateMachine(String name, Handler handler)152         NsdStateMachine(String name, Handler handler) {
153             super(name, handler);
154             addState(mDefaultState);
155                 addState(mDisabledState, mDefaultState);
156                 addState(mEnabledState, mDefaultState);
157             State initialState = mEnabledState;
158             setInitialState(initialState);
159             setLogRecSize(25);
160         }
161 
162         class DefaultState extends State {
163             @Override
processMessage(Message msg)164             public boolean processMessage(Message msg) {
165                 final ClientInfo cInfo;
166                 final int clientId = msg.arg2;
167                 switch (msg.what) {
168                     case NsdManager.REGISTER_CLIENT:
169                         final Pair<NsdServiceConnector, INsdManagerCallback> arg =
170                                 (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
171                         final INsdManagerCallback cb = arg.second;
172                         try {
173                             cb.asBinder().linkToDeath(arg.first, 0);
174                             cInfo = new ClientInfo(cb);
175                             mClients.put(arg.first, cInfo);
176                         } catch (RemoteException e) {
177                             Log.w(TAG, "Client " + clientId + " has already died");
178                         }
179                         break;
180                     case NsdManager.UNREGISTER_CLIENT:
181                         final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
182                         cInfo = mClients.remove(connector);
183                         if (cInfo != null) {
184                             cInfo.expungeAllRequests();
185                             if (cInfo.isLegacy()) {
186                                 mLegacyClientCount -= 1;
187                             }
188                         }
189                         maybeScheduleStop();
190                         break;
191                     case NsdManager.DISCOVER_SERVICES:
192                         cInfo = getClientInfoForReply(msg);
193                         if (cInfo != null) {
194                             cInfo.onDiscoverServicesFailed(
195                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
196                         }
197                        break;
198                     case NsdManager.STOP_DISCOVERY:
199                         cInfo = getClientInfoForReply(msg);
200                         if (cInfo != null) {
201                             cInfo.onStopDiscoveryFailed(
202                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
203                         }
204                         break;
205                     case NsdManager.REGISTER_SERVICE:
206                         cInfo = getClientInfoForReply(msg);
207                         if (cInfo != null) {
208                             cInfo.onRegisterServiceFailed(
209                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
210                         }
211                         break;
212                     case NsdManager.UNREGISTER_SERVICE:
213                         cInfo = getClientInfoForReply(msg);
214                         if (cInfo != null) {
215                             cInfo.onUnregisterServiceFailed(
216                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
217                         }
218                         break;
219                     case NsdManager.RESOLVE_SERVICE:
220                         cInfo = getClientInfoForReply(msg);
221                         if (cInfo != null) {
222                             cInfo.onResolveServiceFailed(
223                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
224                         }
225                         break;
226                     case NsdManager.DAEMON_CLEANUP:
227                         maybeStopDaemon();
228                         break;
229                     // This event should be only sent by the legacy (target SDK < S) clients.
230                     // Mark the sending client as legacy.
231                     case NsdManager.DAEMON_STARTUP:
232                         cInfo = getClientInfoForReply(msg);
233                         if (cInfo != null) {
234                             cancelStop();
235                             cInfo.setLegacy();
236                             mLegacyClientCount += 1;
237                             maybeStartDaemon();
238                         }
239                         break;
240                     default:
241                         Log.e(TAG, "Unhandled " + msg);
242                         return NOT_HANDLED;
243                 }
244                 return HANDLED;
245             }
246 
getClientInfoForReply(Message msg)247             private ClientInfo getClientInfoForReply(Message msg) {
248                 final ListenerArgs args = (ListenerArgs) msg.obj;
249                 return mClients.get(args.connector);
250             }
251         }
252 
253         class DisabledState extends State {
254             @Override
enter()255             public void enter() {
256                 sendNsdStateChangeBroadcast(false);
257             }
258 
259             @Override
processMessage(Message msg)260             public boolean processMessage(Message msg) {
261                 switch (msg.what) {
262                     case NsdManager.ENABLE:
263                         transitionTo(mEnabledState);
264                         break;
265                     default:
266                         return NOT_HANDLED;
267                 }
268                 return HANDLED;
269             }
270         }
271 
272         class EnabledState extends State {
273             @Override
enter()274             public void enter() {
275                 sendNsdStateChangeBroadcast(true);
276             }
277 
278             @Override
exit()279             public void exit() {
280                 // TODO: it is incorrect to stop the daemon without expunging all requests
281                 // and sending error callbacks to clients.
282                 scheduleStop();
283             }
284 
requestLimitReached(ClientInfo clientInfo)285             private boolean requestLimitReached(ClientInfo clientInfo) {
286                 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
287                     if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
288                     return true;
289                 }
290                 return false;
291             }
292 
storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what)293             private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
294                 clientInfo.mClientIds.put(clientId, globalId);
295                 clientInfo.mClientRequests.put(clientId, what);
296                 mIdToClientInfoMap.put(globalId, clientInfo);
297                 // Remove the cleanup event because here comes a new request.
298                 cancelStop();
299             }
300 
removeRequestMap(int clientId, int globalId, ClientInfo clientInfo)301             private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
302                 clientInfo.mClientIds.delete(clientId);
303                 clientInfo.mClientRequests.delete(clientId);
304                 mIdToClientInfoMap.remove(globalId);
305                 maybeScheduleStop();
306             }
307 
308             @Override
processMessage(Message msg)309             public boolean processMessage(Message msg) {
310                 final ClientInfo clientInfo;
311                 final int id;
312                 final int clientId = msg.arg2;
313                 final ListenerArgs args;
314                 switch (msg.what) {
315                     case NsdManager.DISABLE:
316                         //TODO: cleanup clients
317                         transitionTo(mDisabledState);
318                         break;
319                     case NsdManager.DISCOVER_SERVICES:
320                         if (DBG) Log.d(TAG, "Discover services");
321                         args = (ListenerArgs) msg.obj;
322                         clientInfo = mClients.get(args.connector);
323 
324                         if (requestLimitReached(clientInfo)) {
325                             clientInfo.onDiscoverServicesFailed(
326                                     clientId, NsdManager.FAILURE_MAX_LIMIT);
327                             break;
328                         }
329 
330                         maybeStartDaemon();
331                         id = getUniqueId();
332                         if (discoverServices(id, args.serviceInfo)) {
333                             if (DBG) {
334                                 Log.d(TAG, "Discover " + msg.arg2 + " " + id
335                                         + args.serviceInfo.getServiceType());
336                             }
337                             storeRequestMap(clientId, id, clientInfo, msg.what);
338                             clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
339                         } else {
340                             stopServiceDiscovery(id);
341                             clientInfo.onDiscoverServicesFailed(clientId,
342                                     NsdManager.FAILURE_INTERNAL_ERROR);
343                         }
344                         break;
345                     case NsdManager.STOP_DISCOVERY:
346                         if (DBG) Log.d(TAG, "Stop service discovery");
347                         args = (ListenerArgs) msg.obj;
348                         clientInfo = mClients.get(args.connector);
349 
350                         try {
351                             id = clientInfo.mClientIds.get(clientId);
352                         } catch (NullPointerException e) {
353                             clientInfo.onStopDiscoveryFailed(
354                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
355                             break;
356                         }
357                         removeRequestMap(clientId, id, clientInfo);
358                         if (stopServiceDiscovery(id)) {
359                             clientInfo.onStopDiscoverySucceeded(clientId);
360                         } else {
361                             clientInfo.onStopDiscoveryFailed(
362                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
363                         }
364                         break;
365                     case NsdManager.REGISTER_SERVICE:
366                         if (DBG) Log.d(TAG, "Register service");
367                         args = (ListenerArgs) msg.obj;
368                         clientInfo = mClients.get(args.connector);
369                         if (requestLimitReached(clientInfo)) {
370                             clientInfo.onRegisterServiceFailed(
371                                     clientId, NsdManager.FAILURE_MAX_LIMIT);
372                             break;
373                         }
374 
375                         maybeStartDaemon();
376                         id = getUniqueId();
377                         if (registerService(id, args.serviceInfo)) {
378                             if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
379                             storeRequestMap(clientId, id, clientInfo, msg.what);
380                             // Return success after mDns reports success
381                         } else {
382                             unregisterService(id);
383                             clientInfo.onRegisterServiceFailed(
384                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
385                         }
386                         break;
387                     case NsdManager.UNREGISTER_SERVICE:
388                         if (DBG) Log.d(TAG, "unregister service");
389                         args = (ListenerArgs) msg.obj;
390                         clientInfo = mClients.get(args.connector);
391                         if (clientInfo == null) {
392                             Log.e(TAG, "Unknown connector in unregistration");
393                             break;
394                         }
395                         id = clientInfo.mClientIds.get(clientId);
396                         removeRequestMap(clientId, id, clientInfo);
397                         if (unregisterService(id)) {
398                             clientInfo.onUnregisterServiceSucceeded(clientId);
399                         } else {
400                             clientInfo.onUnregisterServiceFailed(
401                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
402                         }
403                         break;
404                     case NsdManager.RESOLVE_SERVICE:
405                         if (DBG) Log.d(TAG, "Resolve service");
406                         args = (ListenerArgs) msg.obj;
407                         clientInfo = mClients.get(args.connector);
408 
409                         if (clientInfo.mResolvedService != null) {
410                             clientInfo.onResolveServiceFailed(
411                                     clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
412                             break;
413                         }
414 
415                         maybeStartDaemon();
416                         id = getUniqueId();
417                         if (resolveService(id, args.serviceInfo)) {
418                             clientInfo.mResolvedService = new NsdServiceInfo();
419                             storeRequestMap(clientId, id, clientInfo, msg.what);
420                         } else {
421                             clientInfo.onResolveServiceFailed(
422                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
423                         }
424                         break;
425                     case MDNS_SERVICE_EVENT:
426                         if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
427                             return NOT_HANDLED;
428                         }
429                         break;
430                     default:
431                         return NOT_HANDLED;
432                 }
433                 return HANDLED;
434             }
435 
handleMDnsServiceEvent(int code, int id, Object obj)436             private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
437                 NsdServiceInfo servInfo;
438                 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
439                 if (clientInfo == null) {
440                     Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
441                     return false;
442                 }
443 
444                 /* This goes in response as msg.arg2 */
445                 int clientId = clientInfo.getClientId(id);
446                 if (clientId < 0) {
447                     // This can happen because of race conditions. For example,
448                     // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
449                     // and we may get in this situation.
450                     Log.d(TAG, String.format("%d for listener id %d that is no longer active",
451                             code, id));
452                     return false;
453                 }
454                 if (DBG) {
455                     Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
456                 }
457                 switch (code) {
458                     case IMDnsEventListener.SERVICE_FOUND: {
459                         final DiscoveryInfo info = (DiscoveryInfo) obj;
460                         final String name = info.serviceName;
461                         final String type = info.registrationType;
462                         servInfo = new NsdServiceInfo(name, type);
463                         final int foundNetId = info.netId;
464                         if (foundNetId == 0L) {
465                             // Ignore services that do not have a Network: they are not usable
466                             // by apps, as they would need privileged permissions to use
467                             // interfaces that do not have an associated Network.
468                             break;
469                         }
470                         setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
471                         clientInfo.onServiceFound(clientId, servInfo);
472                         break;
473                     }
474                     case IMDnsEventListener.SERVICE_LOST: {
475                         final DiscoveryInfo info = (DiscoveryInfo) obj;
476                         final String name = info.serviceName;
477                         final String type = info.registrationType;
478                         final int lostNetId = info.netId;
479                         servInfo = new NsdServiceInfo(name, type);
480                         // The network could be set to null (netId 0) if it was torn down when the
481                         // service is lost
482                         // TODO: avoid returning null in that case, possibly by remembering
483                         // found services on the same interface index and their network at the time
484                         setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
485                         clientInfo.onServiceLost(clientId, servInfo);
486                         break;
487                     }
488                     case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
489                         clientInfo.onDiscoverServicesFailed(
490                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
491                         break;
492                     case IMDnsEventListener.SERVICE_REGISTERED: {
493                         final RegistrationInfo info = (RegistrationInfo) obj;
494                         final String name = info.serviceName;
495                         servInfo = new NsdServiceInfo(name, null /* serviceType */);
496                         clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
497                         break;
498                     }
499                     case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
500                         clientInfo.onRegisterServiceFailed(
501                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
502                         break;
503                     case IMDnsEventListener.SERVICE_RESOLVED: {
504                         final ResolutionInfo info = (ResolutionInfo) obj;
505                         int index = 0;
506                         final String fullName = info.serviceFullName;
507                         while (index < fullName.length() && fullName.charAt(index) != '.') {
508                             if (fullName.charAt(index) == '\\') {
509                                 ++index;
510                             }
511                             ++index;
512                         }
513                         if (index >= fullName.length()) {
514                             Log.e(TAG, "Invalid service found " + fullName);
515                             break;
516                         }
517 
518                         String name = unescape(fullName.substring(0, index));
519                         String rest = fullName.substring(index);
520                         String type = rest.replace(".local.", "");
521 
522                         clientInfo.mResolvedService.setServiceName(name);
523                         clientInfo.mResolvedService.setServiceType(type);
524                         clientInfo.mResolvedService.setPort(info.port);
525                         clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
526                         // Network will be added after SERVICE_GET_ADDR_SUCCESS
527 
528                         stopResolveService(id);
529                         removeRequestMap(clientId, id, clientInfo);
530 
531                         final int id2 = getUniqueId();
532                         if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
533                             storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
534                         } else {
535                             clientInfo.onResolveServiceFailed(
536                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
537                             clientInfo.mResolvedService = null;
538                         }
539                         break;
540                     }
541                     case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
542                         /* NNN resolveId errorCode */
543                         stopResolveService(id);
544                         removeRequestMap(clientId, id, clientInfo);
545                         clientInfo.mResolvedService = null;
546                         clientInfo.onResolveServiceFailed(
547                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
548                         break;
549                     case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
550                         /* NNN resolveId errorCode */
551                         stopGetAddrInfo(id);
552                         removeRequestMap(clientId, id, clientInfo);
553                         clientInfo.mResolvedService = null;
554                         clientInfo.onResolveServiceFailed(
555                                 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
556                         break;
557                     case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
558                         /* NNN resolveId hostname ttl addr interfaceIdx netId */
559                         final GetAddressInfo info = (GetAddressInfo) obj;
560                         final String address = info.address;
561                         final int netId = info.netId;
562                         InetAddress serviceHost = null;
563                         try {
564                             serviceHost = InetAddress.getByName(address);
565                         } catch (UnknownHostException e) {
566                             Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
567                         }
568 
569                         // If the resolved service is on an interface without a network, consider it
570                         // as a failure: it would not be usable by apps as they would need
571                         // privileged permissions.
572                         if (netId != NETID_UNSET && serviceHost != null) {
573                             clientInfo.mResolvedService.setHost(serviceHost);
574                             setServiceNetworkForCallback(clientInfo.mResolvedService,
575                                     netId, info.interfaceIdx);
576                             clientInfo.onResolveServiceSucceeded(
577                                     clientId, clientInfo.mResolvedService);
578                         } else {
579                             clientInfo.onResolveServiceFailed(
580                                     clientId, NsdManager.FAILURE_INTERNAL_ERROR);
581                         }
582                         stopGetAddrInfo(id);
583                         removeRequestMap(clientId, id, clientInfo);
584                         clientInfo.mResolvedService = null;
585                         break;
586                     }
587                     default:
588                         return false;
589                 }
590                 return true;
591             }
592        }
593     }
594 
setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx)595     private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
596         switch (netId) {
597             case NETID_UNSET:
598                 info.setNetwork(null);
599                 break;
600             case INetd.LOCAL_NET_ID:
601                 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
602                 // visible / usable for apps, so do not return it. Store the interface
603                 // index instead, so at least if the client tries to resolve the service
604                 // with that NsdServiceInfo, it will be done on the same interface.
605                 // If they recreate the NsdServiceInfo themselves, resolution would be
606                 // done on all interfaces as before T, which should also work.
607                 info.setNetwork(null);
608                 info.setInterfaceIndex(ifaceIdx);
609                 break;
610             default:
611                 info.setNetwork(new Network(netId));
612         }
613     }
614 
615     // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
616     // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
617     // unescape for getting right service address. See "Notes on DNS Name Escaping" on
618     // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
unescape(String s)619     private String unescape(String s) {
620         StringBuilder sb = new StringBuilder(s.length());
621         for (int i = 0; i < s.length(); ++i) {
622             char c = s.charAt(i);
623             if (c == '\\') {
624                 if (++i >= s.length()) {
625                     Log.e(TAG, "Unexpected end of escape sequence in: " + s);
626                     break;
627                 }
628                 c = s.charAt(i);
629                 if (c != '.' && c != '\\') {
630                     if (i + 2 >= s.length()) {
631                         Log.e(TAG, "Unexpected end of escape sequence in: " + s);
632                         break;
633                     }
634                     c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
635                             + (s.charAt(i + 2) - '0'));
636                     i += 2;
637                 }
638             }
639             sb.append(c);
640         }
641         return sb.toString();
642     }
643 
644     @VisibleForTesting
NsdService(Context ctx, Handler handler, long cleanupDelayMs)645     NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
646         mCleanupDelayMs = cleanupDelayMs;
647         mContext = ctx;
648         mNsdStateMachine = new NsdStateMachine(TAG, handler);
649         mNsdStateMachine.start();
650         mMDnsManager = ctx.getSystemService(MDnsManager.class);
651         mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
652     }
653 
create(Context context)654     public static NsdService create(Context context) {
655         HandlerThread thread = new HandlerThread(TAG);
656         thread.start();
657         Handler handler = new Handler(thread.getLooper());
658         NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
659         return service;
660     }
661 
662     private static class MDnsEventCallback extends IMDnsEventListener.Stub {
663         private final StateMachine mStateMachine;
664 
MDnsEventCallback(StateMachine sm)665         MDnsEventCallback(StateMachine sm) {
666             mStateMachine = sm;
667         }
668 
669         @Override
onServiceRegistrationStatus(final RegistrationInfo status)670         public void onServiceRegistrationStatus(final RegistrationInfo status) {
671             mStateMachine.sendMessage(
672                     MDNS_SERVICE_EVENT, status.result, status.id, status);
673         }
674 
675         @Override
onServiceDiscoveryStatus(final DiscoveryInfo status)676         public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
677             mStateMachine.sendMessage(
678                     MDNS_SERVICE_EVENT, status.result, status.id, status);
679         }
680 
681         @Override
onServiceResolutionStatus(final ResolutionInfo status)682         public void onServiceResolutionStatus(final ResolutionInfo status) {
683             mStateMachine.sendMessage(
684                     MDNS_SERVICE_EVENT, status.result, status.id, status);
685         }
686 
687         @Override
onGettingServiceAddressStatus(final GetAddressInfo status)688         public void onGettingServiceAddressStatus(final GetAddressInfo status) {
689             mStateMachine.sendMessage(
690                     MDNS_SERVICE_EVENT, status.result, status.id, status);
691         }
692 
693         @Override
getInterfaceVersion()694         public int getInterfaceVersion() throws RemoteException {
695             return this.VERSION;
696         }
697 
698         @Override
getInterfaceHash()699         public String getInterfaceHash() throws RemoteException {
700             return this.HASH;
701         }
702     }
703 
704     @Override
connect(INsdManagerCallback cb)705     public INsdServiceConnector connect(INsdManagerCallback cb) {
706         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
707         final INsdServiceConnector connector = new NsdServiceConnector();
708         mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
709                 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
710         return connector;
711     }
712 
713     private static class ListenerArgs {
714         public final NsdServiceConnector connector;
715         public final NsdServiceInfo serviceInfo;
ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo)716         ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
717             this.connector = connector;
718             this.serviceInfo = serviceInfo;
719         }
720     }
721 
722     private class NsdServiceConnector extends INsdServiceConnector.Stub
723             implements IBinder.DeathRecipient  {
724         @Override
registerService(int listenerKey, NsdServiceInfo serviceInfo)725         public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
726             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
727                     NsdManager.REGISTER_SERVICE, 0, listenerKey,
728                     new ListenerArgs(this, serviceInfo)));
729         }
730 
731         @Override
unregisterService(int listenerKey)732         public void unregisterService(int listenerKey) {
733             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
734                     NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
735                     new ListenerArgs(this, null)));
736         }
737 
738         @Override
discoverServices(int listenerKey, NsdServiceInfo serviceInfo)739         public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
740             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
741                     NsdManager.DISCOVER_SERVICES, 0, listenerKey,
742                     new ListenerArgs(this, serviceInfo)));
743         }
744 
745         @Override
stopDiscovery(int listenerKey)746         public void stopDiscovery(int listenerKey) {
747             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
748                     NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
749         }
750 
751         @Override
resolveService(int listenerKey, NsdServiceInfo serviceInfo)752         public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
753             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
754                     NsdManager.RESOLVE_SERVICE, 0, listenerKey,
755                     new ListenerArgs(this, serviceInfo)));
756         }
757 
758         @Override
startDaemon()759         public void startDaemon() {
760             mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
761                     NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
762         }
763 
764         @Override
binderDied()765         public void binderDied() {
766             mNsdStateMachine.sendMessage(
767                     mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
768         }
769     }
770 
sendNsdStateChangeBroadcast(boolean isEnabled)771     private void sendNsdStateChangeBroadcast(boolean isEnabled) {
772         final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
773         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
774         int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
775         intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
776         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
777     }
778 
getUniqueId()779     private int getUniqueId() {
780         if (++mUniqueId == INVALID_ID) return ++mUniqueId;
781         return mUniqueId;
782     }
783 
registerService(int regId, NsdServiceInfo service)784     private boolean registerService(int regId, NsdServiceInfo service) {
785         if (DBG) {
786             Log.d(TAG, "registerService: " + regId + " " + service);
787         }
788         String name = service.getServiceName();
789         String type = service.getServiceType();
790         int port = service.getPort();
791         byte[] textRecord = service.getTxtRecord();
792         final int registerInterface = getNetworkInterfaceIndex(service);
793         if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
794             Log.e(TAG, "Interface to register service on not found");
795             return false;
796         }
797         return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface);
798     }
799 
unregisterService(int regId)800     private boolean unregisterService(int regId) {
801         return mMDnsManager.stopOperation(regId);
802     }
803 
discoverServices(int discoveryId, NsdServiceInfo serviceInfo)804     private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
805         final String type = serviceInfo.getServiceType();
806         final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
807         if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
808             Log.e(TAG, "Interface to discover service on not found");
809             return false;
810         }
811         return mMDnsManager.discover(discoveryId, type, discoverInterface);
812     }
813 
stopServiceDiscovery(int discoveryId)814     private boolean stopServiceDiscovery(int discoveryId) {
815         return mMDnsManager.stopOperation(discoveryId);
816     }
817 
resolveService(int resolveId, NsdServiceInfo service)818     private boolean resolveService(int resolveId, NsdServiceInfo service) {
819         final String name = service.getServiceName();
820         final String type = service.getServiceType();
821         final int resolveInterface = getNetworkInterfaceIndex(service);
822         if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
823             Log.e(TAG, "Interface to resolve service on not found");
824             return false;
825         }
826         return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
827     }
828 
829     /**
830      * Guess the interface to use to resolve or discover a service on a specific network.
831      *
832      * This is an imperfect guess, as for example the network may be gone or not yet fully
833      * registered. This is fine as failing is correct if the network is gone, and a client
834      * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
835      * this is to support the legacy mdnsresponder implementation, which historically resolved
836      * services on an unspecified network.
837      */
getNetworkInterfaceIndex(NsdServiceInfo serviceInfo)838     private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
839         final Network network = serviceInfo.getNetwork();
840         if (network == null) {
841             // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
842             // provided by NsdService from discovery results, and the service was found on an
843             // interface that has no app-usable Network).
844             if (serviceInfo.getInterfaceIndex() != 0) {
845                 return serviceInfo.getInterfaceIndex();
846             }
847             return IFACE_IDX_ANY;
848         }
849 
850         final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
851         if (cm == null) {
852             Log.wtf(TAG, "No ConnectivityManager for resolveService");
853             return IFACE_IDX_ANY;
854         }
855         final LinkProperties lp = cm.getLinkProperties(network);
856         if (lp == null) return IFACE_IDX_ANY;
857 
858         // Only resolve on non-stacked interfaces
859         final NetworkInterface iface;
860         try {
861             iface = NetworkInterface.getByName(lp.getInterfaceName());
862         } catch (SocketException e) {
863             Log.e(TAG, "Error querying interface", e);
864             return IFACE_IDX_ANY;
865         }
866 
867         if (iface == null) {
868             Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
869             return IFACE_IDX_ANY;
870         }
871 
872         return iface.getIndex();
873     }
874 
stopResolveService(int resolveId)875     private boolean stopResolveService(int resolveId) {
876         return mMDnsManager.stopOperation(resolveId);
877     }
878 
getAddrInfo(int resolveId, String hostname, int interfaceIdx)879     private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
880         return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
881     }
882 
stopGetAddrInfo(int resolveId)883     private boolean stopGetAddrInfo(int resolveId) {
884         return mMDnsManager.stopOperation(resolveId);
885     }
886 
887     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)888     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
889         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
890                 != PackageManager.PERMISSION_GRANTED) {
891             pw.println("Permission Denial: can't dump " + TAG
892                     + " due to missing android.permission.DUMP permission");
893             return;
894         }
895 
896         for (ClientInfo client : mClients.values()) {
897             pw.println("Client Info");
898             pw.println(client);
899         }
900 
901         mNsdStateMachine.dump(fd, pw, args);
902     }
903 
904     /* Information tracked per client */
905     private class ClientInfo {
906 
907         private static final int MAX_LIMIT = 10;
908         private final INsdManagerCallback mCb;
909         /* Remembers a resolved service until getaddrinfo completes */
910         private NsdServiceInfo mResolvedService;
911 
912         /* A map from client id to unique id sent to mDns */
913         private final SparseIntArray mClientIds = new SparseIntArray();
914 
915         /* A map from client id to the type of the request we had received */
916         private final SparseIntArray mClientRequests = new SparseIntArray();
917 
918         // The target SDK of this client < Build.VERSION_CODES.S
919         private boolean mIsLegacy = false;
920 
ClientInfo(INsdManagerCallback cb)921         private ClientInfo(INsdManagerCallback cb) {
922             mCb = cb;
923             if (DBG) Log.d(TAG, "New client");
924         }
925 
926         @Override
toString()927         public String toString() {
928             StringBuilder sb = new StringBuilder();
929             sb.append("mResolvedService ").append(mResolvedService).append("\n");
930             sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
931             for(int i = 0; i< mClientIds.size(); i++) {
932                 int clientID = mClientIds.keyAt(i);
933                 sb.append("clientId ").append(clientID).
934                     append(" mDnsId ").append(mClientIds.valueAt(i)).
935                     append(" type ").append(mClientRequests.get(clientID)).append("\n");
936             }
937             return sb.toString();
938         }
939 
isLegacy()940         private boolean isLegacy() {
941             return mIsLegacy;
942         }
943 
setLegacy()944         private void setLegacy() {
945             mIsLegacy = true;
946         }
947 
948         // Remove any pending requests from the global map when we get rid of a client,
949         // and send cancellations to the daemon.
expungeAllRequests()950         private void expungeAllRequests() {
951             int globalId, clientId, i;
952             // TODO: to keep handler responsive, do not clean all requests for that client at once.
953             for (i = 0; i < mClientIds.size(); i++) {
954                 clientId = mClientIds.keyAt(i);
955                 globalId = mClientIds.valueAt(i);
956                 mIdToClientInfoMap.remove(globalId);
957                 if (DBG) {
958                     Log.d(TAG, "Terminating client-ID " + clientId
959                             + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
960                 }
961                 switch (mClientRequests.get(clientId)) {
962                     case NsdManager.DISCOVER_SERVICES:
963                         stopServiceDiscovery(globalId);
964                         break;
965                     case NsdManager.RESOLVE_SERVICE:
966                         stopResolveService(globalId);
967                         break;
968                     case NsdManager.REGISTER_SERVICE:
969                         unregisterService(globalId);
970                         break;
971                     default:
972                         break;
973                 }
974             }
975             mClientIds.clear();
976             mClientRequests.clear();
977         }
978 
979         // mClientIds is a sparse array of listener id -> mDnsClient id.  For a given mDnsClient id,
980         // return the corresponding listener id.  mDnsClient id is also called a global id.
getClientId(final int globalId)981         private int getClientId(final int globalId) {
982             int idx = mClientIds.indexOfValue(globalId);
983             if (idx < 0) {
984                 return idx;
985             }
986             return mClientIds.keyAt(idx);
987         }
988 
onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info)989         void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
990             try {
991                 mCb.onDiscoverServicesStarted(listenerKey, info);
992             } catch (RemoteException e) {
993                 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
994             }
995         }
996 
onDiscoverServicesFailed(int listenerKey, int error)997         void onDiscoverServicesFailed(int listenerKey, int error) {
998             try {
999                 mCb.onDiscoverServicesFailed(listenerKey, error);
1000             } catch (RemoteException e) {
1001                 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
1002             }
1003         }
1004 
onServiceFound(int listenerKey, NsdServiceInfo info)1005         void onServiceFound(int listenerKey, NsdServiceInfo info) {
1006             try {
1007                 mCb.onServiceFound(listenerKey, info);
1008             } catch (RemoteException e) {
1009                 Log.e(TAG, "Error calling onServiceFound(", e);
1010             }
1011         }
1012 
onServiceLost(int listenerKey, NsdServiceInfo info)1013         void onServiceLost(int listenerKey, NsdServiceInfo info) {
1014             try {
1015                 mCb.onServiceLost(listenerKey, info);
1016             } catch (RemoteException e) {
1017                 Log.e(TAG, "Error calling onServiceLost(", e);
1018             }
1019         }
1020 
onStopDiscoveryFailed(int listenerKey, int error)1021         void onStopDiscoveryFailed(int listenerKey, int error) {
1022             try {
1023                 mCb.onStopDiscoveryFailed(listenerKey, error);
1024             } catch (RemoteException e) {
1025                 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
1026             }
1027         }
1028 
onStopDiscoverySucceeded(int listenerKey)1029         void onStopDiscoverySucceeded(int listenerKey) {
1030             try {
1031                 mCb.onStopDiscoverySucceeded(listenerKey);
1032             } catch (RemoteException e) {
1033                 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
1034             }
1035         }
1036 
onRegisterServiceFailed(int listenerKey, int error)1037         void onRegisterServiceFailed(int listenerKey, int error) {
1038             try {
1039                 mCb.onRegisterServiceFailed(listenerKey, error);
1040             } catch (RemoteException e) {
1041                 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
1042             }
1043         }
1044 
onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info)1045         void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1046             try {
1047                 mCb.onRegisterServiceSucceeded(listenerKey, info);
1048             } catch (RemoteException e) {
1049                 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
1050             }
1051         }
1052 
onUnregisterServiceFailed(int listenerKey, int error)1053         void onUnregisterServiceFailed(int listenerKey, int error) {
1054             try {
1055                 mCb.onUnregisterServiceFailed(listenerKey, error);
1056             } catch (RemoteException e) {
1057                 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
1058             }
1059         }
1060 
onUnregisterServiceSucceeded(int listenerKey)1061         void onUnregisterServiceSucceeded(int listenerKey) {
1062             try {
1063                 mCb.onUnregisterServiceSucceeded(listenerKey);
1064             } catch (RemoteException e) {
1065                 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
1066             }
1067         }
1068 
onResolveServiceFailed(int listenerKey, int error)1069         void onResolveServiceFailed(int listenerKey, int error) {
1070             try {
1071                 mCb.onResolveServiceFailed(listenerKey, error);
1072             } catch (RemoteException e) {
1073                 Log.e(TAG, "Error calling onResolveServiceFailed", e);
1074             }
1075         }
1076 
onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info)1077         void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1078             try {
1079                 mCb.onResolveServiceSucceeded(listenerKey, info);
1080             } catch (RemoteException e) {
1081                 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
1082             }
1083         }
1084     }
1085 }
1086