NsdService.java revision 77b987f1a1bb6028a871de01065b94c4cfff0b5c
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, null); 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 onCheckHoldWakeLock(int code) { 626 return false; 627 } 628 629 public boolean onEvent(int code, String raw, String[] cooked) { 630 // TODO: NDC translates a message to a callback, we could enhance NDC to 631 // directly interact with a state machine through messages 632 NativeEvent event = new NativeEvent(code, raw); 633 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event); 634 return true; 635 } 636 } 637 638 private boolean startMDnsDaemon() { 639 if (DBG) Slog.d(TAG, "startMDnsDaemon"); 640 try { 641 mNativeConnector.execute("mdnssd", "start-service"); 642 } catch(NativeDaemonConnectorException e) { 643 Slog.e(TAG, "Failed to start daemon" + e); 644 return false; 645 } 646 return true; 647 } 648 649 private boolean stopMDnsDaemon() { 650 if (DBG) Slog.d(TAG, "stopMDnsDaemon"); 651 try { 652 mNativeConnector.execute("mdnssd", "stop-service"); 653 } catch(NativeDaemonConnectorException e) { 654 Slog.e(TAG, "Failed to start daemon" + e); 655 return false; 656 } 657 return true; 658 } 659 660 private boolean registerService(int regId, NsdServiceInfo service) { 661 if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); 662 try { 663 //Add txtlen and txtdata 664 mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(), 665 service.getServiceType(), service.getPort()); 666 } catch(NativeDaemonConnectorException e) { 667 Slog.e(TAG, "Failed to execute registerService " + e); 668 return false; 669 } 670 return true; 671 } 672 673 private boolean unregisterService(int regId) { 674 if (DBG) Slog.d(TAG, "unregisterService: " + regId); 675 try { 676 mNativeConnector.execute("mdnssd", "stop-register", regId); 677 } catch(NativeDaemonConnectorException e) { 678 Slog.e(TAG, "Failed to execute unregisterService " + e); 679 return false; 680 } 681 return true; 682 } 683 684 private boolean updateService(int regId, DnsSdTxtRecord t) { 685 if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t); 686 try { 687 if (t == null) return false; 688 mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData()); 689 } catch(NativeDaemonConnectorException e) { 690 Slog.e(TAG, "Failed to updateServices " + e); 691 return false; 692 } 693 return true; 694 } 695 696 private boolean discoverServices(int discoveryId, String serviceType) { 697 if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType); 698 try { 699 mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType); 700 } catch(NativeDaemonConnectorException e) { 701 Slog.e(TAG, "Failed to discoverServices " + e); 702 return false; 703 } 704 return true; 705 } 706 707 private boolean stopServiceDiscovery(int discoveryId) { 708 if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId); 709 try { 710 mNativeConnector.execute("mdnssd", "stop-discover", discoveryId); 711 } catch(NativeDaemonConnectorException e) { 712 Slog.e(TAG, "Failed to stopServiceDiscovery " + e); 713 return false; 714 } 715 return true; 716 } 717 718 private boolean resolveService(int resolveId, NsdServiceInfo service) { 719 if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service); 720 try { 721 mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(), 722 service.getServiceType(), "local."); 723 } catch(NativeDaemonConnectorException e) { 724 Slog.e(TAG, "Failed to resolveService " + e); 725 return false; 726 } 727 return true; 728 } 729 730 private boolean stopResolveService(int resolveId) { 731 if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId); 732 try { 733 mNativeConnector.execute("mdnssd", "stop-resolve", resolveId); 734 } catch(NativeDaemonConnectorException e) { 735 Slog.e(TAG, "Failed to stop resolve " + e); 736 return false; 737 } 738 return true; 739 } 740 741 private boolean getAddrInfo(int resolveId, String hostname) { 742 if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId); 743 try { 744 mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname); 745 } catch(NativeDaemonConnectorException e) { 746 Slog.e(TAG, "Failed to getAddrInfo " + e); 747 return false; 748 } 749 return true; 750 } 751 752 private boolean stopGetAddrInfo(int resolveId) { 753 if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId); 754 try { 755 mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId); 756 } catch(NativeDaemonConnectorException e) { 757 Slog.e(TAG, "Failed to stopGetAddrInfo " + e); 758 return false; 759 } 760 return true; 761 } 762 763 @Override 764 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 765 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 766 != PackageManager.PERMISSION_GRANTED) { 767 pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid=" 768 + Binder.getCallingPid() 769 + ", uid=" + Binder.getCallingUid()); 770 return; 771 } 772 773 for (ClientInfo client : mClients.values()) { 774 pw.println("Client Info"); 775 pw.println(client); 776 } 777 778 mNsdStateMachine.dump(fd, pw, args); 779 } 780 781 /* arg2 on the source message has an id that needs to be retained in replies 782 * see NsdManager for details */ 783 private Message obtainMessage(Message srcMsg) { 784 Message msg = Message.obtain(); 785 msg.arg2 = srcMsg.arg2; 786 return msg; 787 } 788 789 private void replyToMessage(Message msg, int what) { 790 if (msg.replyTo == null) return; 791 Message dstMsg = obtainMessage(msg); 792 dstMsg.what = what; 793 mReplyChannel.replyToMessage(msg, dstMsg); 794 } 795 796 private void replyToMessage(Message msg, int what, int arg1) { 797 if (msg.replyTo == null) return; 798 Message dstMsg = obtainMessage(msg); 799 dstMsg.what = what; 800 dstMsg.arg1 = arg1; 801 mReplyChannel.replyToMessage(msg, dstMsg); 802 } 803 804 private void replyToMessage(Message msg, int what, Object obj) { 805 if (msg.replyTo == null) return; 806 Message dstMsg = obtainMessage(msg); 807 dstMsg.what = what; 808 dstMsg.obj = obj; 809 mReplyChannel.replyToMessage(msg, dstMsg); 810 } 811 812 /* Information tracked per client */ 813 private class ClientInfo { 814 815 private static final int MAX_LIMIT = 10; 816 private final AsyncChannel mChannel; 817 private final Messenger mMessenger; 818 /* Remembers a resolved service until getaddrinfo completes */ 819 private NsdServiceInfo mResolvedService; 820 821 /* A map from client id to unique id sent to mDns */ 822 private SparseArray<Integer> mClientIds = new SparseArray<Integer>(); 823 824 private ClientInfo(AsyncChannel c, Messenger m) { 825 mChannel = c; 826 mMessenger = m; 827 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m); 828 } 829 830 @Override 831 public String toString() { 832 StringBuffer sb = new StringBuffer(); 833 sb.append("mChannel ").append(mChannel).append("\n"); 834 sb.append("mMessenger ").append(mMessenger).append("\n"); 835 sb.append("mResolvedService ").append(mResolvedService).append("\n"); 836 for(int i = 0; i< mClientIds.size(); i++) { 837 sb.append("clientId ").append(mClientIds.keyAt(i)); 838 sb.append(" mDnsId ").append(mClientIds.valueAt(i)).append("\n"); 839 } 840 return sb.toString(); 841 } 842 } 843} 844