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