NsdService.java revision 03666c705ddabe0e7c5869ab69c2ca8b964164e9
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 = clientInfo.getClientId(id); 425 if (clientId < 0) { 426 // This can happen because of race conditions. For example, 427 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY, 428 // and we may get in this situation. 429 Slog.d(TAG, "Notification for a listener that is no longer active: " + id); 430 handled = false; 431 return handled; 432 } 433 434 switch (code) { 435 case NativeResponseCode.SERVICE_FOUND: 436 /* NNN uniqueId serviceName regType domain */ 437 if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw); 438 servInfo = new NsdServiceInfo(cooked[2], cooked[3]); 439 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0, 440 clientId, servInfo); 441 break; 442 case NativeResponseCode.SERVICE_LOST: 443 /* NNN uniqueId serviceName regType domain */ 444 if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw); 445 servInfo = new NsdServiceInfo(cooked[2], cooked[3]); 446 clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0, 447 clientId, servInfo); 448 break; 449 case NativeResponseCode.SERVICE_DISCOVERY_FAILED: 450 /* NNN uniqueId errorCode */ 451 if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw); 452 clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED, 453 NsdManager.FAILURE_INTERNAL_ERROR, clientId); 454 break; 455 case NativeResponseCode.SERVICE_REGISTERED: 456 /* NNN regId serviceName regType */ 457 if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw); 458 servInfo = new NsdServiceInfo(cooked[2], null); 459 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED, 460 id, clientId, servInfo); 461 break; 462 case NativeResponseCode.SERVICE_REGISTRATION_FAILED: 463 /* NNN regId errorCode */ 464 if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw); 465 clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED, 466 NsdManager.FAILURE_INTERNAL_ERROR, clientId); 467 break; 468 case NativeResponseCode.SERVICE_UPDATED: 469 /* NNN regId */ 470 break; 471 case NativeResponseCode.SERVICE_UPDATE_FAILED: 472 /* NNN regId errorCode */ 473 break; 474 case NativeResponseCode.SERVICE_RESOLVED: 475 /* NNN resolveId fullName hostName port txtlen txtdata */ 476 if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw); 477 int index = cooked[2].indexOf("."); 478 if (index == -1) { 479 Slog.e(TAG, "Invalid service found " + raw); 480 break; 481 } 482 String name = cooked[2].substring(0, index); 483 String rest = cooked[2].substring(index); 484 String type = rest.replace(".local.", ""); 485 486 clientInfo.mResolvedService.setServiceName(name); 487 clientInfo.mResolvedService.setServiceType(type); 488 clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4])); 489 490 stopResolveService(id); 491 removeRequestMap(clientId, id, clientInfo); 492 493 int id2 = getUniqueId(); 494 if (getAddrInfo(id2, cooked[3])) { 495 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE); 496 } else { 497 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, 498 NsdManager.FAILURE_INTERNAL_ERROR, clientId); 499 clientInfo.mResolvedService = null; 500 } 501 break; 502 case NativeResponseCode.SERVICE_RESOLUTION_FAILED: 503 /* NNN resolveId errorCode */ 504 if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw); 505 stopResolveService(id); 506 removeRequestMap(clientId, id, clientInfo); 507 clientInfo.mResolvedService = null; 508 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, 509 NsdManager.FAILURE_INTERNAL_ERROR, clientId); 510 break; 511 case NativeResponseCode.SERVICE_GET_ADDR_FAILED: 512 /* NNN resolveId errorCode */ 513 stopGetAddrInfo(id); 514 removeRequestMap(clientId, id, clientInfo); 515 clientInfo.mResolvedService = null; 516 if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw); 517 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, 518 NsdManager.FAILURE_INTERNAL_ERROR, clientId); 519 break; 520 case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: 521 /* NNN resolveId hostname ttl addr */ 522 if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw); 523 try { 524 clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); 525 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 526 0, clientId, clientInfo.mResolvedService); 527 } catch (java.net.UnknownHostException e) { 528 clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, 529 NsdManager.FAILURE_INTERNAL_ERROR, clientId); 530 } 531 stopGetAddrInfo(id); 532 removeRequestMap(clientId, id, clientInfo); 533 clientInfo.mResolvedService = null; 534 break; 535 default: 536 handled = false; 537 break; 538 } 539 return handled; 540 } 541 } 542 } 543 544 private NativeDaemonConnector mNativeConnector; 545 private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1); 546 547 private NsdService(Context context) { 548 mContext = context; 549 mContentResolver = context.getContentResolver(); 550 551 mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10, 552 MDNS_TAG, 25, null); 553 554 mNsdStateMachine = new NsdStateMachine(TAG); 555 mNsdStateMachine.start(); 556 557 Thread th = new Thread(mNativeConnector, MDNS_TAG); 558 th.start(); 559 } 560 561 public static NsdService create(Context context) throws InterruptedException { 562 NsdService service = new NsdService(context); 563 service.mNativeDaemonConnected.await(); 564 return service; 565 } 566 567 public Messenger getMessenger() { 568 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, 569 "NsdService"); 570 return new Messenger(mNsdStateMachine.getHandler()); 571 } 572 573 public void setEnabled(boolean enable) { 574 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL, 575 "NsdService"); 576 Settings.Global.putInt(mContentResolver, Settings.Global.NSD_ON, enable ? 1 : 0); 577 if (enable) { 578 mNsdStateMachine.sendMessage(NsdManager.ENABLE); 579 } else { 580 mNsdStateMachine.sendMessage(NsdManager.DISABLE); 581 } 582 } 583 584 private void sendNsdStateChangeBroadcast(boolean enabled) { 585 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED); 586 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 587 if (enabled) { 588 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED); 589 } else { 590 intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED); 591 } 592 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 593 } 594 595 private boolean isNsdEnabled() { 596 boolean ret = Settings.Global.getInt(mContentResolver, Settings.Global.NSD_ON, 1) == 1; 597 if (DBG) Slog.d(TAG, "Network service discovery enabled " + ret); 598 return ret; 599 } 600 601 private int getUniqueId() { 602 if (++mUniqueId == INVALID_ID) return ++mUniqueId; 603 return mUniqueId; 604 } 605 606 /* These should be in sync with system/netd/server/ResponseCode.h */ 607 class NativeResponseCode { 608 public static final int SERVICE_DISCOVERY_FAILED = 602; 609 public static final int SERVICE_FOUND = 603; 610 public static final int SERVICE_LOST = 604; 611 612 public static final int SERVICE_REGISTRATION_FAILED = 605; 613 public static final int SERVICE_REGISTERED = 606; 614 615 public static final int SERVICE_RESOLUTION_FAILED = 607; 616 public static final int SERVICE_RESOLVED = 608; 617 618 public static final int SERVICE_UPDATED = 609; 619 public static final int SERVICE_UPDATE_FAILED = 610; 620 621 public static final int SERVICE_GET_ADDR_FAILED = 611; 622 public static final int SERVICE_GET_ADDR_SUCCESS = 612; 623 } 624 625 private class NativeEvent { 626 final int code; 627 final String raw; 628 629 NativeEvent(int code, String raw) { 630 this.code = code; 631 this.raw = raw; 632 } 633 } 634 635 class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks { 636 public void onDaemonConnected() { 637 mNativeDaemonConnected.countDown(); 638 } 639 640 public boolean onCheckHoldWakeLock(int code) { 641 return false; 642 } 643 644 public boolean onEvent(int code, String raw, String[] cooked) { 645 // TODO: NDC translates a message to a callback, we could enhance NDC to 646 // directly interact with a state machine through messages 647 NativeEvent event = new NativeEvent(code, raw); 648 mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event); 649 return true; 650 } 651 } 652 653 private boolean startMDnsDaemon() { 654 if (DBG) Slog.d(TAG, "startMDnsDaemon"); 655 try { 656 mNativeConnector.execute("mdnssd", "start-service"); 657 } catch(NativeDaemonConnectorException e) { 658 Slog.e(TAG, "Failed to start daemon" + e); 659 return false; 660 } 661 return true; 662 } 663 664 private boolean stopMDnsDaemon() { 665 if (DBG) Slog.d(TAG, "stopMDnsDaemon"); 666 try { 667 mNativeConnector.execute("mdnssd", "stop-service"); 668 } catch(NativeDaemonConnectorException e) { 669 Slog.e(TAG, "Failed to start daemon" + e); 670 return false; 671 } 672 return true; 673 } 674 675 private boolean registerService(int regId, NsdServiceInfo service) { 676 if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); 677 try { 678 Command cmd = new Command("mdnssd", "register", regId, service.getServiceName(), 679 service.getServiceType(), service.getPort()); 680 681 // Add TXT records as additional arguments. 682 Map<String, byte[]> txtRecords = service.getAttributes(); 683 for (String key : txtRecords.keySet()) { 684 try { 685 // TODO: Send encoded TXT record as bytes once NDC/netd supports binary data. 686 cmd.appendArg(String.format(Locale.US, "%s=%s", key, 687 new String(txtRecords.get(key), "UTF_8"))); 688 } catch (UnsupportedEncodingException e) { 689 Slog.e(TAG, "Failed to encode txtRecord " + e); 690 } 691 } 692 693 mNativeConnector.execute(cmd); 694 } catch(NativeDaemonConnectorException e) { 695 Slog.e(TAG, "Failed to execute registerService " + e); 696 return false; 697 } 698 return true; 699 } 700 701 private boolean unregisterService(int regId) { 702 if (DBG) Slog.d(TAG, "unregisterService: " + regId); 703 try { 704 mNativeConnector.execute("mdnssd", "stop-register", regId); 705 } catch(NativeDaemonConnectorException e) { 706 Slog.e(TAG, "Failed to execute unregisterService " + e); 707 return false; 708 } 709 return true; 710 } 711 712 private boolean updateService(int regId, DnsSdTxtRecord t) { 713 if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t); 714 try { 715 if (t == null) return false; 716 mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData()); 717 } catch(NativeDaemonConnectorException e) { 718 Slog.e(TAG, "Failed to updateServices " + e); 719 return false; 720 } 721 return true; 722 } 723 724 private boolean discoverServices(int discoveryId, String serviceType) { 725 if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType); 726 try { 727 mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType); 728 } catch(NativeDaemonConnectorException e) { 729 Slog.e(TAG, "Failed to discoverServices " + e); 730 return false; 731 } 732 return true; 733 } 734 735 private boolean stopServiceDiscovery(int discoveryId) { 736 if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId); 737 try { 738 mNativeConnector.execute("mdnssd", "stop-discover", discoveryId); 739 } catch(NativeDaemonConnectorException e) { 740 Slog.e(TAG, "Failed to stopServiceDiscovery " + e); 741 return false; 742 } 743 return true; 744 } 745 746 private boolean resolveService(int resolveId, NsdServiceInfo service) { 747 if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service); 748 try { 749 mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(), 750 service.getServiceType(), "local."); 751 } catch(NativeDaemonConnectorException e) { 752 Slog.e(TAG, "Failed to resolveService " + e); 753 return false; 754 } 755 return true; 756 } 757 758 private boolean stopResolveService(int resolveId) { 759 if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId); 760 try { 761 mNativeConnector.execute("mdnssd", "stop-resolve", resolveId); 762 } catch(NativeDaemonConnectorException e) { 763 Slog.e(TAG, "Failed to stop resolve " + e); 764 return false; 765 } 766 return true; 767 } 768 769 private boolean getAddrInfo(int resolveId, String hostname) { 770 if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId); 771 try { 772 mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname); 773 } catch(NativeDaemonConnectorException e) { 774 Slog.e(TAG, "Failed to getAddrInfo " + e); 775 return false; 776 } 777 return true; 778 } 779 780 private boolean stopGetAddrInfo(int resolveId) { 781 if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId); 782 try { 783 mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId); 784 } catch(NativeDaemonConnectorException e) { 785 Slog.e(TAG, "Failed to stopGetAddrInfo " + e); 786 return false; 787 } 788 return true; 789 } 790 791 @Override 792 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 793 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 794 != PackageManager.PERMISSION_GRANTED) { 795 pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid=" 796 + Binder.getCallingPid() 797 + ", uid=" + Binder.getCallingUid()); 798 return; 799 } 800 801 for (ClientInfo client : mClients.values()) { 802 pw.println("Client Info"); 803 pw.println(client); 804 } 805 806 mNsdStateMachine.dump(fd, pw, args); 807 } 808 809 /* arg2 on the source message has an id that needs to be retained in replies 810 * see NsdManager for details */ 811 private Message obtainMessage(Message srcMsg) { 812 Message msg = Message.obtain(); 813 msg.arg2 = srcMsg.arg2; 814 return msg; 815 } 816 817 private void replyToMessage(Message msg, int what) { 818 if (msg.replyTo == null) return; 819 Message dstMsg = obtainMessage(msg); 820 dstMsg.what = what; 821 mReplyChannel.replyToMessage(msg, dstMsg); 822 } 823 824 private void replyToMessage(Message msg, int what, int arg1) { 825 if (msg.replyTo == null) return; 826 Message dstMsg = obtainMessage(msg); 827 dstMsg.what = what; 828 dstMsg.arg1 = arg1; 829 mReplyChannel.replyToMessage(msg, dstMsg); 830 } 831 832 private void replyToMessage(Message msg, int what, Object obj) { 833 if (msg.replyTo == null) return; 834 Message dstMsg = obtainMessage(msg); 835 dstMsg.what = what; 836 dstMsg.obj = obj; 837 mReplyChannel.replyToMessage(msg, dstMsg); 838 } 839 840 /* Information tracked per client */ 841 private class ClientInfo { 842 843 private static final int MAX_LIMIT = 10; 844 private final AsyncChannel mChannel; 845 private final Messenger mMessenger; 846 /* Remembers a resolved service until getaddrinfo completes */ 847 private NsdServiceInfo mResolvedService; 848 849 /* A map from client id to unique id sent to mDns */ 850 private SparseArray<Integer> mClientIds = new SparseArray<Integer>(); 851 852 /* A map from client id to the type of the request we had received */ 853 private SparseArray<Integer> mClientRequests = new SparseArray<Integer>(); 854 855 private ClientInfo(AsyncChannel c, Messenger m) { 856 mChannel = c; 857 mMessenger = m; 858 if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m); 859 } 860 861 @Override 862 public String toString() { 863 StringBuffer sb = new StringBuffer(); 864 sb.append("mChannel ").append(mChannel).append("\n"); 865 sb.append("mMessenger ").append(mMessenger).append("\n"); 866 sb.append("mResolvedService ").append(mResolvedService).append("\n"); 867 for(int i = 0; i< mClientIds.size(); i++) { 868 int clientID = mClientIds.keyAt(i); 869 sb.append("clientId ").append(clientID). 870 append(" mDnsId ").append(mClientIds.valueAt(i)). 871 append(" type ").append(mClientRequests.get(clientID)).append("\n"); 872 } 873 return sb.toString(); 874 } 875 876 // Remove any pending requests from the global map when we get rid of a client, 877 // and send cancellations to the daemon. 878 private void expungeAllRequests() { 879 int globalId, clientId, i; 880 for (i = 0; i < mClientIds.size(); i++) { 881 clientId = mClientIds.keyAt(i); 882 globalId = mClientIds.valueAt(i); 883 mIdToClientInfoMap.remove(globalId); 884 if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId + 885 " global-ID " + globalId + " type " + mClientRequests.get(clientId)); 886 switch (mClientRequests.get(clientId)) { 887 case NsdManager.DISCOVER_SERVICES: 888 stopServiceDiscovery(globalId); 889 break; 890 case NsdManager.RESOLVE_SERVICE: 891 stopResolveService(globalId); 892 break; 893 case NsdManager.REGISTER_SERVICE: 894 unregisterService(globalId); 895 break; 896 default: 897 break; 898 } 899 } 900 mClientIds.clear(); 901 mClientRequests.clear(); 902 } 903 904 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id, 905 // return the corresponding listener id. mDnsClient id is also called a global id. 906 private int getClientId(final int globalId) { 907 // This doesn't use mClientIds.indexOfValue because indexOfValue uses == (not .equals) 908 // while also coercing the int primitives to Integer objects. 909 for (int i = 0, nSize = mClientIds.size(); i < nSize; i++) { 910 int mDnsId = mClientIds.valueAt(i); 911 if (globalId == mDnsId) { 912 return mClientIds.keyAt(i); 913 } 914 } 915 return -1; 916 } 917 } 918} 919