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