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