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