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