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