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