• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 android.content.Context;
20 import android.content.ContentResolver;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.database.ContentObserver;
24 import android.net.nsd.NsdServiceInfo;
25 import android.net.nsd.DnsSdTxtRecord;
26 import android.net.nsd.INsdManager;
27 import android.net.nsd.NsdManager;
28 import android.os.Binder;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.Message;
32 import android.os.Messenger;
33 import android.os.IBinder;
34 import android.provider.Settings;
35 import android.util.Slog;
36 import android.util.SparseArray;
37 
38 import java.io.FileDescriptor;
39 import java.io.PrintWriter;
40 import java.net.InetAddress;
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.concurrent.CountDownLatch;
45 
46 import com.android.internal.app.IBatteryStats;
47 import com.android.internal.telephony.TelephonyIntents;
48 import com.android.internal.util.AsyncChannel;
49 import com.android.internal.util.Protocol;
50 import com.android.internal.util.State;
51 import com.android.internal.util.StateMachine;
52 import com.android.server.am.BatteryStatsService;
53 import com.android.server.NativeDaemonConnector.Command;
54 import com.android.internal.R;
55 
56 /**
57  * Network Service Discovery Service handles remote service discovery operation requests by
58  * implementing the INsdManager interface.
59  *
60  * @hide
61  */
62 public class NsdService extends INsdManager.Stub {
63     private static final String TAG = "NsdService";
64     private static final String MDNS_TAG = "mDnsConnector";
65 
66     private static final boolean DBG = true;
67 
68     private Context mContext;
69     private ContentResolver mContentResolver;
70     private NsdStateMachine mNsdStateMachine;
71 
72     /**
73      * Clients receiving asynchronous messages
74      */
75     private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>();
76 
77     /* A map from unique id to client info */
78     private SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<ClientInfo>();
79 
80     private AsyncChannel mReplyChannel = new AsyncChannel();
81 
82     private int INVALID_ID = 0;
83     private int mUniqueId = 1;
84 
85     private static final int BASE = Protocol.BASE_NSD_MANAGER;
86     private static final int CMD_TO_STRING_COUNT = NsdManager.RESOLVE_SERVICE - BASE + 1;
87     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
88 
89     static {
90         sCmdToString[NsdManager.DISCOVER_SERVICES - BASE] = "DISCOVER";
91         sCmdToString[NsdManager.STOP_DISCOVERY - BASE] = "STOP-DISCOVER";
92         sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER";
93         sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER";
94         sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE";
95     }
96 
cmdToString(int cmd)97     private static String cmdToString(int cmd) {
98         cmd -= BASE;
99         if ((cmd >= 0) && (cmd < sCmdToString.length)) {
100             return sCmdToString[cmd];
101         } else {
102             return null;
103         }
104     }
105 
106     private class NsdStateMachine extends StateMachine {
107 
108         private final DefaultState mDefaultState = new DefaultState();
109         private final DisabledState mDisabledState = new DisabledState();
110         private final EnabledState mEnabledState = new EnabledState();
111 
112         @Override
getMessageInfo(Message msg)113         protected String getMessageInfo(Message msg) {
114             return cmdToString(msg.what);
115         }
116 
117         /**
118          * Observes the NSD on/off setting, and takes action when changed.
119          */
registerForNsdSetting()120         private void registerForNsdSetting() {
121             ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
122                 @Override
123                     public void onChange(boolean selfChange) {
124                         if (isNsdEnabled()) {
125                             mNsdStateMachine.sendMessage(NsdManager.ENABLE);
126                         } else {
127                             mNsdStateMachine.sendMessage(NsdManager.DISABLE);
128                         }
129                     }
130             };
131 
132             mContext.getContentResolver().registerContentObserver(
133                     Settings.Secure.getUriFor(Settings.Secure.NSD_ON),
134                     false, contentObserver);
135         }
136 
NsdStateMachine(String name)137         NsdStateMachine(String name) {
138             super(name);
139             addState(mDefaultState);
140                 addState(mDisabledState, mDefaultState);
141                 addState(mEnabledState, mDefaultState);
142             if (isNsdEnabled()) {
143                 setInitialState(mEnabledState);
144             } else {
145                 setInitialState(mDisabledState);
146             }
147             setProcessedMessagesSize(25);
148             registerForNsdSetting();
149         }
150 
151         class DefaultState extends State {
152             @Override
processMessage(Message msg)153             public boolean processMessage(Message msg) {
154                 switch (msg.what) {
155                     case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
156                         if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
157                             AsyncChannel c = (AsyncChannel) msg.obj;
158                             if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
159                             c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
160                             ClientInfo cInfo = new ClientInfo(c, msg.replyTo);
161                             mClients.put(msg.replyTo, cInfo);
162                         } else {
163                             Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
164                         }
165                         break;
166                     case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
167                         if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
168                             Slog.e(TAG, "Send failed, client connection lost");
169                         } else {
170                             if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
171                         }
172                         mClients.remove(msg.replyTo);
173                         break;
174                     case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
175                         AsyncChannel ac = new AsyncChannel();
176                         ac.connect(mContext, getHandler(), msg.replyTo);
177                         break;
178                     case NsdManager.DISCOVER_SERVICES:
179                         replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
180                                 NsdManager.FAILURE_INTERNAL_ERROR);
181                        break;
182                     case NsdManager.STOP_DISCOVERY:
183                        replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
184                                NsdManager.FAILURE_INTERNAL_ERROR);
185                         break;
186                     case NsdManager.REGISTER_SERVICE:
187                         replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
188                                 NsdManager.FAILURE_INTERNAL_ERROR);
189                         break;
190                     case NsdManager.UNREGISTER_SERVICE:
191                         replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
192                                 NsdManager.FAILURE_INTERNAL_ERROR);
193                         break;
194                     case NsdManager.RESOLVE_SERVICE:
195                         replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
196                                 NsdManager.FAILURE_INTERNAL_ERROR);
197                         break;
198                     case NsdManager.NATIVE_DAEMON_EVENT:
199                     default:
200                         Slog.e(TAG, "Unhandled " + msg);
201                         return NOT_HANDLED;
202                 }
203                 return HANDLED;
204             }
205         }
206 
207         class DisabledState extends State {
208             @Override
enter()209             public void enter() {
210                 sendNsdStateChangeBroadcast(false);
211             }
212 
213             @Override
processMessage(Message msg)214             public boolean processMessage(Message msg) {
215                 switch (msg.what) {
216                     case NsdManager.ENABLE:
217                         transitionTo(mEnabledState);
218                         break;
219                     default:
220                         return NOT_HANDLED;
221                 }
222                 return HANDLED;
223             }
224         }
225 
226         class EnabledState extends State {
227             @Override
enter()228             public void enter() {
229                 sendNsdStateChangeBroadcast(true);
230                 if (mClients.size() > 0) {
231                     startMDnsDaemon();
232                 }
233             }
234 
235             @Override
exit()236             public void exit() {
237                 if (mClients.size() > 0) {
238                     stopMDnsDaemon();
239                 }
240             }
241 
requestLimitReached(ClientInfo clientInfo)242             private boolean requestLimitReached(ClientInfo clientInfo) {
243                 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
244                     if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
245                     return true;
246                 }
247                 return false;
248             }
249 
storeRequestMap(int clientId, int globalId, ClientInfo clientInfo)250             private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
251                 clientInfo.mClientIds.put(clientId, globalId);
252                 mIdToClientInfoMap.put(globalId, clientInfo);
253             }
254 
removeRequestMap(int clientId, int globalId, ClientInfo clientInfo)255             private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
256                 clientInfo.mClientIds.remove(clientId);
257                 mIdToClientInfoMap.remove(globalId);
258             }
259 
260             @Override
processMessage(Message msg)261             public boolean processMessage(Message msg) {
262                 ClientInfo clientInfo;
263                 NsdServiceInfo servInfo;
264                 boolean result = HANDLED;
265                 int id;
266                 switch (msg.what) {
267                   case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
268                         //First client
269                         if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL &&
270                                 mClients.size() == 0) {
271                             startMDnsDaemon();
272                         }
273                         result = NOT_HANDLED;
274                         break;
275                     case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
276                         //Last client
277                         if (mClients.size() == 1) {
278                             stopMDnsDaemon();
279                         }
280                         result = NOT_HANDLED;
281                         break;
282                     case NsdManager.DISABLE:
283                         //TODO: cleanup clients
284                         transitionTo(mDisabledState);
285                         break;
286                     case NsdManager.DISCOVER_SERVICES:
287                         if (DBG) Slog.d(TAG, "Discover services");
288                         servInfo = (NsdServiceInfo) msg.obj;
289                         clientInfo = mClients.get(msg.replyTo);
290 
291                         if (requestLimitReached(clientInfo)) {
292                             replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
293                                     NsdManager.FAILURE_MAX_LIMIT);
294                             break;
295                         }
296 
297                         id = getUniqueId();
298                         if (discoverServices(id, servInfo.getServiceType())) {
299                             if (DBG) {
300                                 Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
301                                         servInfo.getServiceType());
302                             }
303                             storeRequestMap(msg.arg2, id, clientInfo);
304                             replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
305                         } else {
306                             stopServiceDiscovery(id);
307                             replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
308                                     NsdManager.FAILURE_INTERNAL_ERROR);
309                         }
310                         break;
311                     case NsdManager.STOP_DISCOVERY:
312                         if (DBG) Slog.d(TAG, "Stop service discovery");
313                         clientInfo = mClients.get(msg.replyTo);
314 
315                         try {
316                             id = clientInfo.mClientIds.get(msg.arg2).intValue();
317                         } catch (NullPointerException e) {
318                             replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
319                                     NsdManager.FAILURE_INTERNAL_ERROR);
320                             break;
321                         }
322                         removeRequestMap(msg.arg2, id, clientInfo);
323                         if (stopServiceDiscovery(id)) {
324                             replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
325                         } else {
326                             replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
327                                     NsdManager.FAILURE_INTERNAL_ERROR);
328                         }
329                         break;
330                     case NsdManager.REGISTER_SERVICE:
331                         if (DBG) Slog.d(TAG, "Register service");
332                         clientInfo = mClients.get(msg.replyTo);
333                         if (requestLimitReached(clientInfo)) {
334                             replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
335                                     NsdManager.FAILURE_MAX_LIMIT);
336                             break;
337                         }
338 
339                         id = getUniqueId();
340                         if (registerService(id, (NsdServiceInfo) msg.obj)) {
341                             if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
342                             storeRequestMap(msg.arg2, id, clientInfo);
343                             // Return success after mDns reports success
344                         } else {
345                             unregisterService(id);
346                             replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
347                                     NsdManager.FAILURE_INTERNAL_ERROR);
348                         }
349                         break;
350                     case NsdManager.UNREGISTER_SERVICE:
351                         if (DBG) Slog.d(TAG, "unregister service");
352                         clientInfo = mClients.get(msg.replyTo);
353                         try {
354                             id = clientInfo.mClientIds.get(msg.arg2).intValue();
355                         } catch (NullPointerException e) {
356                             replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
357                                     NsdManager.FAILURE_INTERNAL_ERROR);
358                             break;
359                         }
360                         removeRequestMap(msg.arg2, id, clientInfo);
361                         if (unregisterService(id)) {
362                             replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
363                         } else {
364                             replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
365                                     NsdManager.FAILURE_INTERNAL_ERROR);
366                         }
367                         break;
368                     case NsdManager.RESOLVE_SERVICE:
369                         if (DBG) Slog.d(TAG, "Resolve service");
370                         servInfo = (NsdServiceInfo) msg.obj;
371                         clientInfo = mClients.get(msg.replyTo);
372 
373 
374                         if (clientInfo.mResolvedService != null) {
375                             replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
376                                     NsdManager.FAILURE_ALREADY_ACTIVE);
377                             break;
378                         }
379 
380                         id = getUniqueId();
381                         if (resolveService(id, servInfo)) {
382                             clientInfo.mResolvedService = new NsdServiceInfo();
383                             storeRequestMap(msg.arg2, id, clientInfo);
384                         } else {
385                             replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
386                                     NsdManager.FAILURE_INTERNAL_ERROR);
387                         }
388                         break;
389                     case NsdManager.NATIVE_DAEMON_EVENT:
390                         NativeEvent event = (NativeEvent) msg.obj;
391                         handleNativeEvent(event.code, event.raw,
392                                 NativeDaemonEvent.unescapeArgs(event.raw));
393                         break;
394                     default:
395                         result = NOT_HANDLED;
396                         break;
397                 }
398                 return result;
399             }
400        }
401     }
402 
403     private NativeDaemonConnector mNativeConnector;
404     private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
405 
NsdService(Context context)406     private NsdService(Context context) {
407         mContext = context;
408         mContentResolver = context.getContentResolver();
409 
410         mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
411                 MDNS_TAG, 25);
412 
413         mNsdStateMachine = new NsdStateMachine(TAG);
414         mNsdStateMachine.start();
415 
416         Thread th = new Thread(mNativeConnector, MDNS_TAG);
417         th.start();
418     }
419 
create(Context context)420     public static NsdService create(Context context) throws InterruptedException {
421         NsdService service = new NsdService(context);
422         service.mNativeDaemonConnected.await();
423         return service;
424     }
425 
getMessenger()426     public Messenger getMessenger() {
427         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
428             "NsdService");
429         return new Messenger(mNsdStateMachine.getHandler());
430     }
431 
setEnabled(boolean enable)432     public void setEnabled(boolean enable) {
433         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
434                 "NsdService");
435         Settings.Secure.putInt(mContentResolver, Settings.Secure.NSD_ON, enable ? 1 : 0);
436         if (enable) {
437             mNsdStateMachine.sendMessage(NsdManager.ENABLE);
438         } else {
439             mNsdStateMachine.sendMessage(NsdManager.DISABLE);
440         }
441     }
442 
sendNsdStateChangeBroadcast(boolean enabled)443     private void sendNsdStateChangeBroadcast(boolean enabled) {
444         final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
445         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
446         if (enabled) {
447             intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED);
448         } else {
449             intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
450         }
451         mContext.sendStickyBroadcast(intent);
452     }
453 
isNsdEnabled()454     private boolean isNsdEnabled() {
455         boolean ret = Settings.Secure.getInt(mContentResolver, Settings.Secure.NSD_ON, 1) == 1;
456         if (DBG) Slog.d(TAG, "Network service discovery enabled " + ret);
457         return ret;
458     }
459 
getUniqueId()460     private int getUniqueId() {
461         if (++mUniqueId == INVALID_ID) return ++mUniqueId;
462         return mUniqueId;
463     }
464 
465     /* These should be in sync with system/netd/mDnsResponseCode.h */
466     class NativeResponseCode {
467         public static final int SERVICE_DISCOVERY_FAILED    =   602;
468         public static final int SERVICE_FOUND               =   603;
469         public static final int SERVICE_LOST                =   604;
470 
471         public static final int SERVICE_REGISTRATION_FAILED =   605;
472         public static final int SERVICE_REGISTERED          =   606;
473 
474         public static final int SERVICE_RESOLUTION_FAILED   =   607;
475         public static final int SERVICE_RESOLVED            =   608;
476 
477         public static final int SERVICE_UPDATED             =   609;
478         public static final int SERVICE_UPDATE_FAILED       =   610;
479 
480         public static final int SERVICE_GET_ADDR_FAILED     =   611;
481         public static final int SERVICE_GET_ADDR_SUCCESS    =   612;
482     }
483 
484     private class NativeEvent {
485         int code;
486         String raw;
487 
NativeEvent(int code, String raw)488         NativeEvent(int code, String raw) {
489             this.code = code;
490             this.raw = raw;
491         }
492     }
493 
494     class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
onDaemonConnected()495         public void onDaemonConnected() {
496             mNativeDaemonConnected.countDown();
497         }
498 
onEvent(int code, String raw, String[] cooked)499         public boolean onEvent(int code, String raw, String[] cooked) {
500             // TODO: NDC translates a message to a callback, we could enhance NDC to
501             // directly interact with a state machine through messages
502             NativeEvent event = new NativeEvent(code, raw);
503             mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
504             return true;
505         }
506     }
507 
handleNativeEvent(int code, String raw, String[] cooked)508     private void handleNativeEvent(int code, String raw, String[] cooked) {
509         NsdServiceInfo servInfo;
510         int id = Integer.parseInt(cooked[1]);
511         ClientInfo clientInfo = mIdToClientInfoMap.get(id);
512         if (clientInfo == null) {
513             Slog.e(TAG, "Unique id with no client mapping: " + id);
514             return;
515         }
516 
517         /* This goes in response as msg.arg2 */
518         int clientId = -1;
519         int keyId = clientInfo.mClientIds.indexOfValue(id);
520         if (keyId != -1) {
521             clientId = clientInfo.mClientIds.keyAt(keyId);
522         }
523         switch (code) {
524             case NativeResponseCode.SERVICE_FOUND:
525                 /* NNN uniqueId serviceName regType domain */
526                 if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
527                 servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
528                 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
529                         clientId, servInfo);
530                 break;
531             case NativeResponseCode.SERVICE_LOST:
532                 /* NNN uniqueId serviceName regType domain */
533                 if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
534                 servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
535                 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
536                         clientId, servInfo);
537                 break;
538             case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
539                 /* NNN uniqueId errorCode */
540                 if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
541                 clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
542                         NsdManager.FAILURE_INTERNAL_ERROR, clientId);
543                 break;
544             case NativeResponseCode.SERVICE_REGISTERED:
545                 /* NNN regId serviceName regType */
546                 if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
547                 servInfo = new NsdServiceInfo(cooked[2], null, null);
548                 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
549                         id, clientId, servInfo);
550                 break;
551             case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
552                 /* NNN regId errorCode */
553                 if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
554                 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
555                         NsdManager.FAILURE_INTERNAL_ERROR, clientId);
556                 break;
557             case NativeResponseCode.SERVICE_UPDATED:
558                 /* NNN regId */
559                 break;
560             case NativeResponseCode.SERVICE_UPDATE_FAILED:
561                 /* NNN regId errorCode */
562                 break;
563             case NativeResponseCode.SERVICE_RESOLVED:
564                 /* NNN resolveId fullName hostName port txtlen txtdata */
565                 if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
566                 int index = cooked[2].indexOf(".");
567                 if (index == -1) {
568                     Slog.e(TAG, "Invalid service found " + raw);
569                     break;
570                 }
571                 String name = cooked[2].substring(0, index);
572                 String rest = cooked[2].substring(index);
573                 String type = rest.replace(".local.", "");
574 
575                 clientInfo.mResolvedService.setServiceName(name);
576                 clientInfo.mResolvedService.setServiceType(type);
577                 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
578 
579                 stopResolveService(id);
580                 if (!getAddrInfo(id, cooked[3])) {
581                     clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
582                             NsdManager.FAILURE_INTERNAL_ERROR, clientId);
583                     mIdToClientInfoMap.remove(id);
584                     clientInfo.mResolvedService = null;
585                 }
586                 break;
587             case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
588                 /* NNN resolveId errorCode */
589                 if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
590                 stopResolveService(id);
591                 mIdToClientInfoMap.remove(id);
592                 clientInfo.mResolvedService = null;
593                 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
594                         NsdManager.FAILURE_INTERNAL_ERROR, clientId);
595                 break;
596             case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
597                 /* NNN resolveId errorCode */
598                 stopGetAddrInfo(id);
599                 mIdToClientInfoMap.remove(id);
600                 clientInfo.mResolvedService = null;
601                 if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
602                 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
603                         NsdManager.FAILURE_INTERNAL_ERROR, clientId);
604                 break;
605             case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
606                 /* NNN resolveId hostname ttl addr */
607                 if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
608                 try {
609                     clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
610                     clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
611                             0, clientId, clientInfo.mResolvedService);
612                 } catch (java.net.UnknownHostException e) {
613                     clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
614                             NsdManager.FAILURE_INTERNAL_ERROR, clientId);
615                 }
616                 stopGetAddrInfo(id);
617                 mIdToClientInfoMap.remove(id);
618                 clientInfo.mResolvedService = null;
619                 break;
620             default:
621                 break;
622         }
623     }
624 
startMDnsDaemon()625     private boolean startMDnsDaemon() {
626         if (DBG) Slog.d(TAG, "startMDnsDaemon");
627         try {
628             mNativeConnector.execute("mdnssd", "start-service");
629         } catch(NativeDaemonConnectorException e) {
630             Slog.e(TAG, "Failed to start daemon" + e);
631             return false;
632         }
633         return true;
634     }
635 
stopMDnsDaemon()636     private boolean stopMDnsDaemon() {
637         if (DBG) Slog.d(TAG, "stopMDnsDaemon");
638         try {
639             mNativeConnector.execute("mdnssd", "stop-service");
640         } catch(NativeDaemonConnectorException e) {
641             Slog.e(TAG, "Failed to start daemon" + e);
642             return false;
643         }
644         return true;
645     }
646 
registerService(int regId, NsdServiceInfo service)647     private boolean registerService(int regId, NsdServiceInfo service) {
648         if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
649         try {
650             //Add txtlen and txtdata
651             mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
652                     service.getServiceType(), service.getPort());
653         } catch(NativeDaemonConnectorException e) {
654             Slog.e(TAG, "Failed to execute registerService " + e);
655             return false;
656         }
657         return true;
658     }
659 
unregisterService(int regId)660     private boolean unregisterService(int regId) {
661         if (DBG) Slog.d(TAG, "unregisterService: " + regId);
662         try {
663             mNativeConnector.execute("mdnssd", "stop-register", regId);
664         } catch(NativeDaemonConnectorException e) {
665             Slog.e(TAG, "Failed to execute unregisterService " + e);
666             return false;
667         }
668         return true;
669     }
670 
updateService(int regId, DnsSdTxtRecord t)671     private boolean updateService(int regId, DnsSdTxtRecord t) {
672         if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t);
673         try {
674             if (t == null) return false;
675             mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
676         } catch(NativeDaemonConnectorException e) {
677             Slog.e(TAG, "Failed to updateServices " + e);
678             return false;
679         }
680         return true;
681     }
682 
discoverServices(int discoveryId, String serviceType)683     private boolean discoverServices(int discoveryId, String serviceType) {
684         if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType);
685         try {
686             mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
687         } catch(NativeDaemonConnectorException e) {
688             Slog.e(TAG, "Failed to discoverServices " + e);
689             return false;
690         }
691         return true;
692     }
693 
stopServiceDiscovery(int discoveryId)694     private boolean stopServiceDiscovery(int discoveryId) {
695         if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId);
696         try {
697             mNativeConnector.execute("mdnssd", "stop-discover", discoveryId);
698         } catch(NativeDaemonConnectorException e) {
699             Slog.e(TAG, "Failed to stopServiceDiscovery " + e);
700             return false;
701         }
702         return true;
703     }
704 
resolveService(int resolveId, NsdServiceInfo service)705     private boolean resolveService(int resolveId, NsdServiceInfo service) {
706         if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
707         try {
708             mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
709                     service.getServiceType(), "local.");
710         } catch(NativeDaemonConnectorException e) {
711             Slog.e(TAG, "Failed to resolveService " + e);
712             return false;
713         }
714         return true;
715     }
716 
stopResolveService(int resolveId)717     private boolean stopResolveService(int resolveId) {
718         if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId);
719         try {
720             mNativeConnector.execute("mdnssd", "stop-resolve", resolveId);
721         } catch(NativeDaemonConnectorException e) {
722             Slog.e(TAG, "Failed to stop resolve " + e);
723             return false;
724         }
725         return true;
726     }
727 
getAddrInfo(int resolveId, String hostname)728     private boolean getAddrInfo(int resolveId, String hostname) {
729         if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId);
730         try {
731             mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname);
732         } catch(NativeDaemonConnectorException e) {
733             Slog.e(TAG, "Failed to getAddrInfo " + e);
734             return false;
735         }
736         return true;
737     }
738 
stopGetAddrInfo(int resolveId)739     private boolean stopGetAddrInfo(int resolveId) {
740         if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId);
741         try {
742             mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId);
743         } catch(NativeDaemonConnectorException e) {
744             Slog.e(TAG, "Failed to stopGetAddrInfo " + e);
745             return false;
746         }
747         return true;
748     }
749 
750     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)751     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
752         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
753                 != PackageManager.PERMISSION_GRANTED) {
754             pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
755                     + Binder.getCallingPid()
756                     + ", uid=" + Binder.getCallingUid());
757             return;
758         }
759 
760         for (ClientInfo client : mClients.values()) {
761             pw.println("Client Info");
762             pw.println(client);
763         }
764 
765         mNsdStateMachine.dump(fd, pw, args);
766     }
767 
768     /* arg2 on the source message has an id that needs to be retained in replies
769      * see NsdManager for details */
obtainMessage(Message srcMsg)770     private Message obtainMessage(Message srcMsg) {
771         Message msg = Message.obtain();
772         msg.arg2 = srcMsg.arg2;
773         return msg;
774     }
775 
replyToMessage(Message msg, int what)776     private void replyToMessage(Message msg, int what) {
777         if (msg.replyTo == null) return;
778         Message dstMsg = obtainMessage(msg);
779         dstMsg.what = what;
780         mReplyChannel.replyToMessage(msg, dstMsg);
781     }
782 
replyToMessage(Message msg, int what, int arg1)783     private void replyToMessage(Message msg, int what, int arg1) {
784         if (msg.replyTo == null) return;
785         Message dstMsg = obtainMessage(msg);
786         dstMsg.what = what;
787         dstMsg.arg1 = arg1;
788         mReplyChannel.replyToMessage(msg, dstMsg);
789     }
790 
replyToMessage(Message msg, int what, Object obj)791     private void replyToMessage(Message msg, int what, Object obj) {
792         if (msg.replyTo == null) return;
793         Message dstMsg = obtainMessage(msg);
794         dstMsg.what = what;
795         dstMsg.obj = obj;
796         mReplyChannel.replyToMessage(msg, dstMsg);
797     }
798 
799     /* Information tracked per client */
800     private class ClientInfo {
801 
802         private static final int MAX_LIMIT = 10;
803         private AsyncChannel mChannel;
804         private Messenger mMessenger;
805         /* Remembers a resolved service until getaddrinfo completes */
806         private NsdServiceInfo mResolvedService;
807 
808         /* A map from client id to unique id sent to mDns */
809         private SparseArray<Integer> mClientIds = new SparseArray<Integer>();
810 
ClientInfo(AsyncChannel c, Messenger m)811         private ClientInfo(AsyncChannel c, Messenger m) {
812             mChannel = c;
813             mMessenger = m;
814             if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
815         }
816 
817         @Override
toString()818         public String toString() {
819             StringBuffer sb = new StringBuffer();
820             sb.append("mChannel ").append(mChannel).append("\n");
821             sb.append("mMessenger ").append(mMessenger).append("\n");
822             sb.append("mResolvedService ").append(mResolvedService).append("\n");
823             for(int i = 0; i< mClientIds.size(); i++) {
824                 sb.append("clientId ").append(mClientIds.keyAt(i));
825                 sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n");
826             }
827             return sb.toString();
828         }
829     }
830 }
831