Home | History | Annotate | Download | only in server
      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 
     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
    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          */
    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 
    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
    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
    209             public void enter() {
    210                 sendNsdStateChangeBroadcast(false);
    211             }
    212 
    213             @Override
    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
    228             public void enter() {
    229                 sendNsdStateChangeBroadcast(true);
    230                 if (mClients.size() > 0) {
    231                     startMDnsDaemon();
    232                 }
    233             }
    234 
    235             @Override
    236             public void exit() {
    237                 if (mClients.size() > 0) {
    238                     stopMDnsDaemon();
    239                 }
    240             }
    241 
    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 
    250             private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
    251                 clientInfo.mClientIds.put(clientId, globalId);
    252                 mIdToClientInfoMap.put(globalId, clientInfo);
    253             }
    254 
    255             private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
    256                 clientInfo.mClientIds.remove(clientId);
    257                 mIdToClientInfoMap.remove(globalId);
    258             }
    259 
    260             @Override
    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 
    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 
    420     public static NsdService create(Context context) throws InterruptedException {
    421         NsdService service = new NsdService(context);
    422         service.mNativeDaemonConnected.await();
    423         return service;
    424     }
    425 
    426     public Messenger getMessenger() {
    427         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
    428             "NsdService");
    429         return new Messenger(mNsdStateMachine.getHandler());
    430     }
    431 
    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 
    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 
    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 
    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 
    488         NativeEvent(int code, String raw) {
    489             this.code = code;
    490             this.raw = raw;
    491         }
    492     }
    493 
    494     class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
    495         public void onDaemonConnected() {
    496             mNativeDaemonConnected.countDown();
    497         }
    498 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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 */
    770     private Message obtainMessage(Message srcMsg) {
    771         Message msg = Message.obtain();
    772         msg.arg2 = srcMsg.arg2;
    773         return msg;
    774     }
    775 
    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 
    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 
    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 
    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
    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