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