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