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                 }
    421                 switch (code) {
    422                     case NativeResponseCode.SERVICE_FOUND:
    423                         /* NNN uniqueId serviceName regType domain */
    424                         if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw);
    425                         servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
    426                         clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
    427                                 clientId, servInfo);
    428                         break;
    429                     case NativeResponseCode.SERVICE_LOST:
    430                         /* NNN uniqueId serviceName regType domain */
    431                         if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw);
    432                         servInfo = new NsdServiceInfo(cooked[2], cooked[3], null);
    433                         clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
    434                                 clientId, servInfo);
    435                         break;
    436                     case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
    437                         /* NNN uniqueId errorCode */
    438                         if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw);
    439                         clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
    440                                 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
    441                         break;
    442                     case NativeResponseCode.SERVICE_REGISTERED:
    443                         /* NNN regId serviceName regType */
    444                         if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw);
    445                         servInfo = new NsdServiceInfo(cooked[2], null, null);
    446                         clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
    447                                 id, clientId, servInfo);
    448                         break;
    449                     case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
    450                         /* NNN regId errorCode */
    451                         if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw);
    452                         clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
    453                                NsdManager.FAILURE_INTERNAL_ERROR, clientId);
    454                         break;
    455                     case NativeResponseCode.SERVICE_UPDATED:
    456                         /* NNN regId */
    457                         break;
    458                     case NativeResponseCode.SERVICE_UPDATE_FAILED:
    459                         /* NNN regId errorCode */
    460                         break;
    461                     case NativeResponseCode.SERVICE_RESOLVED:
    462                         /* NNN resolveId fullName hostName port txtlen txtdata */
    463                         if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw);
    464                         int index = cooked[2].indexOf(".");
    465                         if (index == -1) {
    466                             Slog.e(TAG, "Invalid service found " + raw);
    467                             break;
    468                         }
    469                         String name = cooked[2].substring(0, index);
    470                         String rest = cooked[2].substring(index);
    471                         String type = rest.replace(".local.", "");
    472 
    473                         clientInfo.mResolvedService.setServiceName(name);
    474                         clientInfo.mResolvedService.setServiceType(type);
    475                         clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
    476 
    477                         stopResolveService(id);
    478                         if (!getAddrInfo(id, cooked[3])) {
    479                             clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
    480                                     NsdManager.FAILURE_INTERNAL_ERROR, clientId);
    481                             removeRequestMap(clientId, id, clientInfo);
    482                             clientInfo.mResolvedService = null;
    483                         }
    484                         break;
    485                     case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
    486                         /* NNN resolveId errorCode */
    487                         if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
    488                         stopResolveService(id);
    489                         removeRequestMap(clientId, id, clientInfo);
    490                         clientInfo.mResolvedService = null;
    491                         clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
    492                                 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
    493                         break;
    494                     case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
    495                         /* NNN resolveId errorCode */
    496                         stopGetAddrInfo(id);
    497                         removeRequestMap(clientId, id, clientInfo);
    498                         clientInfo.mResolvedService = null;
    499                         if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw);
    500                         clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
    501                                 NsdManager.FAILURE_INTERNAL_ERROR, clientId);
    502                         break;
    503                     case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
    504                         /* NNN resolveId hostname ttl addr */
    505                         if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw);
    506                         try {
    507                             clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
    508                             clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
    509                                    0, clientId, clientInfo.mResolvedService);
    510                         } catch (java.net.UnknownHostException e) {
    511                             clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
    512                                     NsdManager.FAILURE_INTERNAL_ERROR, clientId);
    513                         }
    514                         stopGetAddrInfo(id);
    515                         removeRequestMap(clientId, id, clientInfo);
    516                         clientInfo.mResolvedService = null;
    517                         break;
    518                     default:
    519                         handled = false;
    520                         break;
    521                 }
    522                 return handled;
    523             }
    524        }
    525     }
    526 
    527     private NativeDaemonConnector mNativeConnector;
    528     private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
    529 
    530     private NsdService(Context context) {
    531         mContext = context;
    532         mContentResolver = context.getContentResolver();
    533 
    534         mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
    535                 MDNS_TAG, 25);
    536 
    537         mNsdStateMachine = new NsdStateMachine(TAG);
    538         mNsdStateMachine.start();
    539 
    540         Thread th = new Thread(mNativeConnector, MDNS_TAG);
    541         th.start();
    542     }
    543 
    544     public static NsdService create(Context context) throws InterruptedException {
    545         NsdService service = new NsdService(context);
    546         service.mNativeDaemonConnected.await();
    547         return service;
    548     }
    549 
    550     public Messenger getMessenger() {
    551         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
    552             "NsdService");
    553         return new Messenger(mNsdStateMachine.getHandler());
    554     }
    555 
    556     public void setEnabled(boolean enable) {
    557         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
    558                 "NsdService");
    559         Settings.Global.putInt(mContentResolver, Settings.Global.NSD_ON, enable ? 1 : 0);
    560         if (enable) {
    561             mNsdStateMachine.sendMessage(NsdManager.ENABLE);
    562         } else {
    563             mNsdStateMachine.sendMessage(NsdManager.DISABLE);
    564         }
    565     }
    566 
    567     private void sendNsdStateChangeBroadcast(boolean enabled) {
    568         final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
    569         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    570         if (enabled) {
    571             intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED);
    572         } else {
    573             intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
    574         }
    575         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    576     }
    577 
    578     private boolean isNsdEnabled() {
    579         boolean ret = Settings.Global.getInt(mContentResolver, Settings.Global.NSD_ON, 1) == 1;
    580         if (DBG) Slog.d(TAG, "Network service discovery enabled " + ret);
    581         return ret;
    582     }
    583 
    584     private int getUniqueId() {
    585         if (++mUniqueId == INVALID_ID) return ++mUniqueId;
    586         return mUniqueId;
    587     }
    588 
    589     /* These should be in sync with system/netd/mDnsResponseCode.h */
    590     class NativeResponseCode {
    591         public static final int SERVICE_DISCOVERY_FAILED    =   602;
    592         public static final int SERVICE_FOUND               =   603;
    593         public static final int SERVICE_LOST                =   604;
    594 
    595         public static final int SERVICE_REGISTRATION_FAILED =   605;
    596         public static final int SERVICE_REGISTERED          =   606;
    597 
    598         public static final int SERVICE_RESOLUTION_FAILED   =   607;
    599         public static final int SERVICE_RESOLVED            =   608;
    600 
    601         public static final int SERVICE_UPDATED             =   609;
    602         public static final int SERVICE_UPDATE_FAILED       =   610;
    603 
    604         public static final int SERVICE_GET_ADDR_FAILED     =   611;
    605         public static final int SERVICE_GET_ADDR_SUCCESS    =   612;
    606     }
    607 
    608     private class NativeEvent {
    609         final int code;
    610         final String raw;
    611 
    612         NativeEvent(int code, String raw) {
    613             this.code = code;
    614             this.raw = raw;
    615         }
    616     }
    617 
    618     class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
    619         public void onDaemonConnected() {
    620             mNativeDaemonConnected.countDown();
    621         }
    622 
    623         public boolean onEvent(int code, String raw, String[] cooked) {
    624             // TODO: NDC translates a message to a callback, we could enhance NDC to
    625             // directly interact with a state machine through messages
    626             NativeEvent event = new NativeEvent(code, raw);
    627             mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
    628             return true;
    629         }
    630     }
    631 
    632     private boolean startMDnsDaemon() {
    633         if (DBG) Slog.d(TAG, "startMDnsDaemon");
    634         try {
    635             mNativeConnector.execute("mdnssd", "start-service");
    636         } catch(NativeDaemonConnectorException e) {
    637             Slog.e(TAG, "Failed to start daemon" + e);
    638             return false;
    639         }
    640         return true;
    641     }
    642 
    643     private boolean stopMDnsDaemon() {
    644         if (DBG) Slog.d(TAG, "stopMDnsDaemon");
    645         try {
    646             mNativeConnector.execute("mdnssd", "stop-service");
    647         } catch(NativeDaemonConnectorException e) {
    648             Slog.e(TAG, "Failed to start daemon" + e);
    649             return false;
    650         }
    651         return true;
    652     }
    653 
    654     private boolean registerService(int regId, NsdServiceInfo service) {
    655         if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
    656         try {
    657             //Add txtlen and txtdata
    658             mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
    659                     service.getServiceType(), service.getPort());
    660         } catch(NativeDaemonConnectorException e) {
    661             Slog.e(TAG, "Failed to execute registerService " + e);
    662             return false;
    663         }
    664         return true;
    665     }
    666 
    667     private boolean unregisterService(int regId) {
    668         if (DBG) Slog.d(TAG, "unregisterService: " + regId);
    669         try {
    670             mNativeConnector.execute("mdnssd", "stop-register", regId);
    671         } catch(NativeDaemonConnectorException e) {
    672             Slog.e(TAG, "Failed to execute unregisterService " + e);
    673             return false;
    674         }
    675         return true;
    676     }
    677 
    678     private boolean updateService(int regId, DnsSdTxtRecord t) {
    679         if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t);
    680         try {
    681             if (t == null) return false;
    682             mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
    683         } catch(NativeDaemonConnectorException e) {
    684             Slog.e(TAG, "Failed to updateServices " + e);
    685             return false;
    686         }
    687         return true;
    688     }
    689 
    690     private boolean discoverServices(int discoveryId, String serviceType) {
    691         if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType);
    692         try {
    693             mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
    694         } catch(NativeDaemonConnectorException e) {
    695             Slog.e(TAG, "Failed to discoverServices " + e);
    696             return false;
    697         }
    698         return true;
    699     }
    700 
    701     private boolean stopServiceDiscovery(int discoveryId) {
    702         if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId);
    703         try {
    704             mNativeConnector.execute("mdnssd", "stop-discover", discoveryId);
    705         } catch(NativeDaemonConnectorException e) {
    706             Slog.e(TAG, "Failed to stopServiceDiscovery " + e);
    707             return false;
    708         }
    709         return true;
    710     }
    711 
    712     private boolean resolveService(int resolveId, NsdServiceInfo service) {
    713         if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service);
    714         try {
    715             mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(),
    716                     service.getServiceType(), "local.");
    717         } catch(NativeDaemonConnectorException e) {
    718             Slog.e(TAG, "Failed to resolveService " + e);
    719             return false;
    720         }
    721         return true;
    722     }
    723 
    724     private boolean stopResolveService(int resolveId) {
    725         if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId);
    726         try {
    727             mNativeConnector.execute("mdnssd", "stop-resolve", resolveId);
    728         } catch(NativeDaemonConnectorException e) {
    729             Slog.e(TAG, "Failed to stop resolve " + e);
    730             return false;
    731         }
    732         return true;
    733     }
    734 
    735     private boolean getAddrInfo(int resolveId, String hostname) {
    736         if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId);
    737         try {
    738             mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname);
    739         } catch(NativeDaemonConnectorException e) {
    740             Slog.e(TAG, "Failed to getAddrInfo " + e);
    741             return false;
    742         }
    743         return true;
    744     }
    745 
    746     private boolean stopGetAddrInfo(int resolveId) {
    747         if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId);
    748         try {
    749             mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId);
    750         } catch(NativeDaemonConnectorException e) {
    751             Slog.e(TAG, "Failed to stopGetAddrInfo " + e);
    752             return false;
    753         }
    754         return true;
    755     }
    756 
    757     @Override
    758     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    759         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    760                 != PackageManager.PERMISSION_GRANTED) {
    761             pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
    762                     + Binder.getCallingPid()
    763                     + ", uid=" + Binder.getCallingUid());
    764             return;
    765         }
    766 
    767         for (ClientInfo client : mClients.values()) {
    768             pw.println("Client Info");
    769             pw.println(client);
    770         }
    771 
    772         mNsdStateMachine.dump(fd, pw, args);
    773     }
    774 
    775     /* arg2 on the source message has an id that needs to be retained in replies
    776      * see NsdManager for details */
    777     private Message obtainMessage(Message srcMsg) {
    778         Message msg = Message.obtain();
    779         msg.arg2 = srcMsg.arg2;
    780         return msg;
    781     }
    782 
    783     private void replyToMessage(Message msg, int what) {
    784         if (msg.replyTo == null) return;
    785         Message dstMsg = obtainMessage(msg);
    786         dstMsg.what = what;
    787         mReplyChannel.replyToMessage(msg, dstMsg);
    788     }
    789 
    790     private void replyToMessage(Message msg, int what, int arg1) {
    791         if (msg.replyTo == null) return;
    792         Message dstMsg = obtainMessage(msg);
    793         dstMsg.what = what;
    794         dstMsg.arg1 = arg1;
    795         mReplyChannel.replyToMessage(msg, dstMsg);
    796     }
    797 
    798     private void replyToMessage(Message msg, int what, Object obj) {
    799         if (msg.replyTo == null) return;
    800         Message dstMsg = obtainMessage(msg);
    801         dstMsg.what = what;
    802         dstMsg.obj = obj;
    803         mReplyChannel.replyToMessage(msg, dstMsg);
    804     }
    805 
    806     /* Information tracked per client */
    807     private class ClientInfo {
    808 
    809         private static final int MAX_LIMIT = 10;
    810         private final AsyncChannel mChannel;
    811         private final Messenger mMessenger;
    812         /* Remembers a resolved service until getaddrinfo completes */
    813         private NsdServiceInfo mResolvedService;
    814 
    815         /* A map from client id to unique id sent to mDns */
    816         private SparseArray<Integer> mClientIds = new SparseArray<Integer>();
    817 
    818         private ClientInfo(AsyncChannel c, Messenger m) {
    819             mChannel = c;
    820             mMessenger = m;
    821             if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
    822         }
    823 
    824         @Override
    825         public String toString() {
    826             StringBuffer sb = new StringBuffer();
    827             sb.append("mChannel ").append(mChannel).append("\n");
    828             sb.append("mMessenger ").append(mMessenger).append("\n");
    829             sb.append("mResolvedService ").append(mResolvedService).append("\n");
    830             for(int i = 0; i< mClientIds.size(); i++) {
    831                 sb.append("clientId ").append(mClientIds.keyAt(i));
    832                 sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n");
    833             }
    834             return sb.toString();
    835         }
    836     }
    837 }
    838