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