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