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