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