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