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