1 /* 2 * Copyright (C) 2021 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 static android.net.ConnectivityManager.NETID_UNSET; 20 import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT; 21 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.net.ConnectivityManager; 26 import android.net.INetd; 27 import android.net.LinkProperties; 28 import android.net.Network; 29 import android.net.mdns.aidl.DiscoveryInfo; 30 import android.net.mdns.aidl.GetAddressInfo; 31 import android.net.mdns.aidl.IMDnsEventListener; 32 import android.net.mdns.aidl.RegistrationInfo; 33 import android.net.mdns.aidl.ResolutionInfo; 34 import android.net.nsd.INsdManager; 35 import android.net.nsd.INsdManagerCallback; 36 import android.net.nsd.INsdServiceConnector; 37 import android.net.nsd.MDnsManager; 38 import android.net.nsd.NsdManager; 39 import android.net.nsd.NsdServiceInfo; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.IBinder; 43 import android.os.Message; 44 import android.os.RemoteException; 45 import android.os.UserHandle; 46 import android.util.Log; 47 import android.util.Pair; 48 import android.util.SparseArray; 49 import android.util.SparseIntArray; 50 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.internal.util.State; 53 import com.android.internal.util.StateMachine; 54 55 import java.io.FileDescriptor; 56 import java.io.PrintWriter; 57 import java.net.InetAddress; 58 import java.net.NetworkInterface; 59 import java.net.SocketException; 60 import java.net.UnknownHostException; 61 import java.util.HashMap; 62 63 /** 64 * Network Service Discovery Service handles remote service discovery operation requests by 65 * implementing the INsdManager interface. 66 * 67 * @hide 68 */ 69 public class NsdService extends INsdManager.Stub { 70 private static final String TAG = "NsdService"; 71 private static final String MDNS_TAG = "mDnsConnector"; 72 73 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 74 private static final long CLEANUP_DELAY_MS = 10000; 75 private static final int IFACE_IDX_ANY = 0; 76 77 private final Context mContext; 78 private final NsdStateMachine mNsdStateMachine; 79 private final MDnsManager mMDnsManager; 80 private final MDnsEventCallback mMDnsEventCallback; 81 // WARNING : Accessing this value in any thread is not safe, it must only be changed in the 82 // state machine thread. If change this outside state machine, it will need to introduce 83 // synchronization. 84 private boolean mIsDaemonStarted = false; 85 86 /** 87 * Clients receiving asynchronous messages 88 */ 89 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>(); 90 91 /* A map from unique id to client info */ 92 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>(); 93 94 private final long mCleanupDelayMs; 95 96 private static final int INVALID_ID = 0; 97 private int mUniqueId = 1; 98 // The count of the connected legacy clients. 99 private int mLegacyClientCount = 0; 100 101 private class NsdStateMachine extends StateMachine { 102 103 private final DefaultState mDefaultState = new DefaultState(); 104 private final DisabledState mDisabledState = new DisabledState(); 105 private final EnabledState mEnabledState = new EnabledState(); 106 107 @Override getWhatToString(int what)108 protected String getWhatToString(int what) { 109 return NsdManager.nameOf(what); 110 } 111 maybeStartDaemon()112 private void maybeStartDaemon() { 113 if (mIsDaemonStarted) { 114 if (DBG) Log.d(TAG, "Daemon is already started."); 115 return; 116 } 117 mMDnsManager.registerEventListener(mMDnsEventCallback); 118 mMDnsManager.startDaemon(); 119 mIsDaemonStarted = true; 120 maybeScheduleStop(); 121 } 122 maybeStopDaemon()123 private void maybeStopDaemon() { 124 if (!mIsDaemonStarted) { 125 if (DBG) Log.d(TAG, "Daemon has not been started."); 126 return; 127 } 128 mMDnsManager.unregisterEventListener(mMDnsEventCallback); 129 mMDnsManager.stopDaemon(); 130 mIsDaemonStarted = false; 131 } 132 isAnyRequestActive()133 private boolean isAnyRequestActive() { 134 return mIdToClientInfoMap.size() != 0; 135 } 136 scheduleStop()137 private void scheduleStop() { 138 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs); 139 } maybeScheduleStop()140 private void maybeScheduleStop() { 141 // The native daemon should stay alive and can't be cleanup 142 // if any legacy client connected. 143 if (!isAnyRequestActive() && mLegacyClientCount == 0) { 144 scheduleStop(); 145 } 146 } 147 cancelStop()148 private void cancelStop() { 149 this.removeMessages(NsdManager.DAEMON_CLEANUP); 150 } 151 NsdStateMachine(String name, Handler handler)152 NsdStateMachine(String name, Handler handler) { 153 super(name, handler); 154 addState(mDefaultState); 155 addState(mDisabledState, mDefaultState); 156 addState(mEnabledState, mDefaultState); 157 State initialState = mEnabledState; 158 setInitialState(initialState); 159 setLogRecSize(25); 160 } 161 162 class DefaultState extends State { 163 @Override processMessage(Message msg)164 public boolean processMessage(Message msg) { 165 final ClientInfo cInfo; 166 final int clientId = msg.arg2; 167 switch (msg.what) { 168 case NsdManager.REGISTER_CLIENT: 169 final Pair<NsdServiceConnector, INsdManagerCallback> arg = 170 (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj; 171 final INsdManagerCallback cb = arg.second; 172 try { 173 cb.asBinder().linkToDeath(arg.first, 0); 174 cInfo = new ClientInfo(cb); 175 mClients.put(arg.first, cInfo); 176 } catch (RemoteException e) { 177 Log.w(TAG, "Client " + clientId + " has already died"); 178 } 179 break; 180 case NsdManager.UNREGISTER_CLIENT: 181 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj; 182 cInfo = mClients.remove(connector); 183 if (cInfo != null) { 184 cInfo.expungeAllRequests(); 185 if (cInfo.isLegacy()) { 186 mLegacyClientCount -= 1; 187 } 188 } 189 maybeScheduleStop(); 190 break; 191 case NsdManager.DISCOVER_SERVICES: 192 cInfo = getClientInfoForReply(msg); 193 if (cInfo != null) { 194 cInfo.onDiscoverServicesFailed( 195 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 196 } 197 break; 198 case NsdManager.STOP_DISCOVERY: 199 cInfo = getClientInfoForReply(msg); 200 if (cInfo != null) { 201 cInfo.onStopDiscoveryFailed( 202 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 203 } 204 break; 205 case NsdManager.REGISTER_SERVICE: 206 cInfo = getClientInfoForReply(msg); 207 if (cInfo != null) { 208 cInfo.onRegisterServiceFailed( 209 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 210 } 211 break; 212 case NsdManager.UNREGISTER_SERVICE: 213 cInfo = getClientInfoForReply(msg); 214 if (cInfo != null) { 215 cInfo.onUnregisterServiceFailed( 216 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 217 } 218 break; 219 case NsdManager.RESOLVE_SERVICE: 220 cInfo = getClientInfoForReply(msg); 221 if (cInfo != null) { 222 cInfo.onResolveServiceFailed( 223 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 224 } 225 break; 226 case NsdManager.DAEMON_CLEANUP: 227 maybeStopDaemon(); 228 break; 229 // This event should be only sent by the legacy (target SDK < S) clients. 230 // Mark the sending client as legacy. 231 case NsdManager.DAEMON_STARTUP: 232 cInfo = getClientInfoForReply(msg); 233 if (cInfo != null) { 234 cancelStop(); 235 cInfo.setLegacy(); 236 mLegacyClientCount += 1; 237 maybeStartDaemon(); 238 } 239 break; 240 default: 241 Log.e(TAG, "Unhandled " + msg); 242 return NOT_HANDLED; 243 } 244 return HANDLED; 245 } 246 getClientInfoForReply(Message msg)247 private ClientInfo getClientInfoForReply(Message msg) { 248 final ListenerArgs args = (ListenerArgs) msg.obj; 249 return mClients.get(args.connector); 250 } 251 } 252 253 class DisabledState extends State { 254 @Override enter()255 public void enter() { 256 sendNsdStateChangeBroadcast(false); 257 } 258 259 @Override processMessage(Message msg)260 public boolean processMessage(Message msg) { 261 switch (msg.what) { 262 case NsdManager.ENABLE: 263 transitionTo(mEnabledState); 264 break; 265 default: 266 return NOT_HANDLED; 267 } 268 return HANDLED; 269 } 270 } 271 272 class EnabledState extends State { 273 @Override enter()274 public void enter() { 275 sendNsdStateChangeBroadcast(true); 276 } 277 278 @Override exit()279 public void exit() { 280 // TODO: it is incorrect to stop the daemon without expunging all requests 281 // and sending error callbacks to clients. 282 scheduleStop(); 283 } 284 requestLimitReached(ClientInfo clientInfo)285 private boolean requestLimitReached(ClientInfo clientInfo) { 286 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) { 287 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo); 288 return true; 289 } 290 return false; 291 } 292 storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what)293 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) { 294 clientInfo.mClientIds.put(clientId, globalId); 295 clientInfo.mClientRequests.put(clientId, what); 296 mIdToClientInfoMap.put(globalId, clientInfo); 297 // Remove the cleanup event because here comes a new request. 298 cancelStop(); 299 } 300 removeRequestMap(int clientId, int globalId, ClientInfo clientInfo)301 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) { 302 clientInfo.mClientIds.delete(clientId); 303 clientInfo.mClientRequests.delete(clientId); 304 mIdToClientInfoMap.remove(globalId); 305 maybeScheduleStop(); 306 } 307 308 @Override processMessage(Message msg)309 public boolean processMessage(Message msg) { 310 final ClientInfo clientInfo; 311 final int id; 312 final int clientId = msg.arg2; 313 final ListenerArgs args; 314 switch (msg.what) { 315 case NsdManager.DISABLE: 316 //TODO: cleanup clients 317 transitionTo(mDisabledState); 318 break; 319 case NsdManager.DISCOVER_SERVICES: 320 if (DBG) Log.d(TAG, "Discover services"); 321 args = (ListenerArgs) msg.obj; 322 clientInfo = mClients.get(args.connector); 323 324 if (requestLimitReached(clientInfo)) { 325 clientInfo.onDiscoverServicesFailed( 326 clientId, NsdManager.FAILURE_MAX_LIMIT); 327 break; 328 } 329 330 maybeStartDaemon(); 331 id = getUniqueId(); 332 if (discoverServices(id, args.serviceInfo)) { 333 if (DBG) { 334 Log.d(TAG, "Discover " + msg.arg2 + " " + id 335 + args.serviceInfo.getServiceType()); 336 } 337 storeRequestMap(clientId, id, clientInfo, msg.what); 338 clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo); 339 } else { 340 stopServiceDiscovery(id); 341 clientInfo.onDiscoverServicesFailed(clientId, 342 NsdManager.FAILURE_INTERNAL_ERROR); 343 } 344 break; 345 case NsdManager.STOP_DISCOVERY: 346 if (DBG) Log.d(TAG, "Stop service discovery"); 347 args = (ListenerArgs) msg.obj; 348 clientInfo = mClients.get(args.connector); 349 350 try { 351 id = clientInfo.mClientIds.get(clientId); 352 } catch (NullPointerException e) { 353 clientInfo.onStopDiscoveryFailed( 354 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 355 break; 356 } 357 removeRequestMap(clientId, id, clientInfo); 358 if (stopServiceDiscovery(id)) { 359 clientInfo.onStopDiscoverySucceeded(clientId); 360 } else { 361 clientInfo.onStopDiscoveryFailed( 362 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 363 } 364 break; 365 case NsdManager.REGISTER_SERVICE: 366 if (DBG) Log.d(TAG, "Register service"); 367 args = (ListenerArgs) msg.obj; 368 clientInfo = mClients.get(args.connector); 369 if (requestLimitReached(clientInfo)) { 370 clientInfo.onRegisterServiceFailed( 371 clientId, NsdManager.FAILURE_MAX_LIMIT); 372 break; 373 } 374 375 maybeStartDaemon(); 376 id = getUniqueId(); 377 if (registerService(id, args.serviceInfo)) { 378 if (DBG) Log.d(TAG, "Register " + clientId + " " + id); 379 storeRequestMap(clientId, id, clientInfo, msg.what); 380 // Return success after mDns reports success 381 } else { 382 unregisterService(id); 383 clientInfo.onRegisterServiceFailed( 384 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 385 } 386 break; 387 case NsdManager.UNREGISTER_SERVICE: 388 if (DBG) Log.d(TAG, "unregister service"); 389 args = (ListenerArgs) msg.obj; 390 clientInfo = mClients.get(args.connector); 391 if (clientInfo == null) { 392 Log.e(TAG, "Unknown connector in unregistration"); 393 break; 394 } 395 id = clientInfo.mClientIds.get(clientId); 396 removeRequestMap(clientId, id, clientInfo); 397 if (unregisterService(id)) { 398 clientInfo.onUnregisterServiceSucceeded(clientId); 399 } else { 400 clientInfo.onUnregisterServiceFailed( 401 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 402 } 403 break; 404 case NsdManager.RESOLVE_SERVICE: 405 if (DBG) Log.d(TAG, "Resolve service"); 406 args = (ListenerArgs) msg.obj; 407 clientInfo = mClients.get(args.connector); 408 409 if (clientInfo.mResolvedService != null) { 410 clientInfo.onResolveServiceFailed( 411 clientId, NsdManager.FAILURE_ALREADY_ACTIVE); 412 break; 413 } 414 415 maybeStartDaemon(); 416 id = getUniqueId(); 417 if (resolveService(id, args.serviceInfo)) { 418 clientInfo.mResolvedService = new NsdServiceInfo(); 419 storeRequestMap(clientId, id, clientInfo, msg.what); 420 } else { 421 clientInfo.onResolveServiceFailed( 422 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 423 } 424 break; 425 case MDNS_SERVICE_EVENT: 426 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) { 427 return NOT_HANDLED; 428 } 429 break; 430 default: 431 return NOT_HANDLED; 432 } 433 return HANDLED; 434 } 435 handleMDnsServiceEvent(int code, int id, Object obj)436 private boolean handleMDnsServiceEvent(int code, int id, Object obj) { 437 NsdServiceInfo servInfo; 438 ClientInfo clientInfo = mIdToClientInfoMap.get(id); 439 if (clientInfo == null) { 440 Log.e(TAG, String.format("id %d for %d has no client mapping", id, code)); 441 return false; 442 } 443 444 /* This goes in response as msg.arg2 */ 445 int clientId = clientInfo.getClientId(id); 446 if (clientId < 0) { 447 // This can happen because of race conditions. For example, 448 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY, 449 // and we may get in this situation. 450 Log.d(TAG, String.format("%d for listener id %d that is no longer active", 451 code, id)); 452 return false; 453 } 454 if (DBG) { 455 Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id)); 456 } 457 switch (code) { 458 case IMDnsEventListener.SERVICE_FOUND: { 459 final DiscoveryInfo info = (DiscoveryInfo) obj; 460 final String name = info.serviceName; 461 final String type = info.registrationType; 462 servInfo = new NsdServiceInfo(name, type); 463 final int foundNetId = info.netId; 464 if (foundNetId == 0L) { 465 // Ignore services that do not have a Network: they are not usable 466 // by apps, as they would need privileged permissions to use 467 // interfaces that do not have an associated Network. 468 break; 469 } 470 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx); 471 clientInfo.onServiceFound(clientId, servInfo); 472 break; 473 } 474 case IMDnsEventListener.SERVICE_LOST: { 475 final DiscoveryInfo info = (DiscoveryInfo) obj; 476 final String name = info.serviceName; 477 final String type = info.registrationType; 478 final int lostNetId = info.netId; 479 servInfo = new NsdServiceInfo(name, type); 480 // The network could be set to null (netId 0) if it was torn down when the 481 // service is lost 482 // TODO: avoid returning null in that case, possibly by remembering 483 // found services on the same interface index and their network at the time 484 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx); 485 clientInfo.onServiceLost(clientId, servInfo); 486 break; 487 } 488 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED: 489 clientInfo.onDiscoverServicesFailed( 490 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 491 break; 492 case IMDnsEventListener.SERVICE_REGISTERED: { 493 final RegistrationInfo info = (RegistrationInfo) obj; 494 final String name = info.serviceName; 495 servInfo = new NsdServiceInfo(name, null /* serviceType */); 496 clientInfo.onRegisterServiceSucceeded(clientId, servInfo); 497 break; 498 } 499 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED: 500 clientInfo.onRegisterServiceFailed( 501 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 502 break; 503 case IMDnsEventListener.SERVICE_RESOLVED: { 504 final ResolutionInfo info = (ResolutionInfo) obj; 505 int index = 0; 506 final String fullName = info.serviceFullName; 507 while (index < fullName.length() && fullName.charAt(index) != '.') { 508 if (fullName.charAt(index) == '\\') { 509 ++index; 510 } 511 ++index; 512 } 513 if (index >= fullName.length()) { 514 Log.e(TAG, "Invalid service found " + fullName); 515 break; 516 } 517 518 String name = unescape(fullName.substring(0, index)); 519 String rest = fullName.substring(index); 520 String type = rest.replace(".local.", ""); 521 522 clientInfo.mResolvedService.setServiceName(name); 523 clientInfo.mResolvedService.setServiceType(type); 524 clientInfo.mResolvedService.setPort(info.port); 525 clientInfo.mResolvedService.setTxtRecords(info.txtRecord); 526 // Network will be added after SERVICE_GET_ADDR_SUCCESS 527 528 stopResolveService(id); 529 removeRequestMap(clientId, id, clientInfo); 530 531 final int id2 = getUniqueId(); 532 if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) { 533 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE); 534 } else { 535 clientInfo.onResolveServiceFailed( 536 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 537 clientInfo.mResolvedService = null; 538 } 539 break; 540 } 541 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED: 542 /* NNN resolveId errorCode */ 543 stopResolveService(id); 544 removeRequestMap(clientId, id, clientInfo); 545 clientInfo.mResolvedService = null; 546 clientInfo.onResolveServiceFailed( 547 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 548 break; 549 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED: 550 /* NNN resolveId errorCode */ 551 stopGetAddrInfo(id); 552 removeRequestMap(clientId, id, clientInfo); 553 clientInfo.mResolvedService = null; 554 clientInfo.onResolveServiceFailed( 555 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 556 break; 557 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: { 558 /* NNN resolveId hostname ttl addr interfaceIdx netId */ 559 final GetAddressInfo info = (GetAddressInfo) obj; 560 final String address = info.address; 561 final int netId = info.netId; 562 InetAddress serviceHost = null; 563 try { 564 serviceHost = InetAddress.getByName(address); 565 } catch (UnknownHostException e) { 566 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e); 567 } 568 569 // If the resolved service is on an interface without a network, consider it 570 // as a failure: it would not be usable by apps as they would need 571 // privileged permissions. 572 if (netId != NETID_UNSET && serviceHost != null) { 573 clientInfo.mResolvedService.setHost(serviceHost); 574 setServiceNetworkForCallback(clientInfo.mResolvedService, 575 netId, info.interfaceIdx); 576 clientInfo.onResolveServiceSucceeded( 577 clientId, clientInfo.mResolvedService); 578 } else { 579 clientInfo.onResolveServiceFailed( 580 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 581 } 582 stopGetAddrInfo(id); 583 removeRequestMap(clientId, id, clientInfo); 584 clientInfo.mResolvedService = null; 585 break; 586 } 587 default: 588 return false; 589 } 590 return true; 591 } 592 } 593 } 594 setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx)595 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) { 596 switch (netId) { 597 case NETID_UNSET: 598 info.setNetwork(null); 599 break; 600 case INetd.LOCAL_NET_ID: 601 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally 602 // visible / usable for apps, so do not return it. Store the interface 603 // index instead, so at least if the client tries to resolve the service 604 // with that NsdServiceInfo, it will be done on the same interface. 605 // If they recreate the NsdServiceInfo themselves, resolution would be 606 // done on all interfaces as before T, which should also work. 607 info.setNetwork(null); 608 info.setInterfaceIndex(ifaceIdx); 609 break; 610 default: 611 info.setNetwork(new Network(netId)); 612 } 613 } 614 615 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable 616 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name 617 // unescape for getting right service address. See "Notes on DNS Name Escaping" on 618 // external/mdnsresponder/mDNSShared/dns_sd.h for more details. unescape(String s)619 private String unescape(String s) { 620 StringBuilder sb = new StringBuilder(s.length()); 621 for (int i = 0; i < s.length(); ++i) { 622 char c = s.charAt(i); 623 if (c == '\\') { 624 if (++i >= s.length()) { 625 Log.e(TAG, "Unexpected end of escape sequence in: " + s); 626 break; 627 } 628 c = s.charAt(i); 629 if (c != '.' && c != '\\') { 630 if (i + 2 >= s.length()) { 631 Log.e(TAG, "Unexpected end of escape sequence in: " + s); 632 break; 633 } 634 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10 635 + (s.charAt(i + 2) - '0')); 636 i += 2; 637 } 638 } 639 sb.append(c); 640 } 641 return sb.toString(); 642 } 643 644 @VisibleForTesting NsdService(Context ctx, Handler handler, long cleanupDelayMs)645 NsdService(Context ctx, Handler handler, long cleanupDelayMs) { 646 mCleanupDelayMs = cleanupDelayMs; 647 mContext = ctx; 648 mNsdStateMachine = new NsdStateMachine(TAG, handler); 649 mNsdStateMachine.start(); 650 mMDnsManager = ctx.getSystemService(MDnsManager.class); 651 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine); 652 } 653 create(Context context)654 public static NsdService create(Context context) { 655 HandlerThread thread = new HandlerThread(TAG); 656 thread.start(); 657 Handler handler = new Handler(thread.getLooper()); 658 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS); 659 return service; 660 } 661 662 private static class MDnsEventCallback extends IMDnsEventListener.Stub { 663 private final StateMachine mStateMachine; 664 MDnsEventCallback(StateMachine sm)665 MDnsEventCallback(StateMachine sm) { 666 mStateMachine = sm; 667 } 668 669 @Override onServiceRegistrationStatus(final RegistrationInfo status)670 public void onServiceRegistrationStatus(final RegistrationInfo status) { 671 mStateMachine.sendMessage( 672 MDNS_SERVICE_EVENT, status.result, status.id, status); 673 } 674 675 @Override onServiceDiscoveryStatus(final DiscoveryInfo status)676 public void onServiceDiscoveryStatus(final DiscoveryInfo status) { 677 mStateMachine.sendMessage( 678 MDNS_SERVICE_EVENT, status.result, status.id, status); 679 } 680 681 @Override onServiceResolutionStatus(final ResolutionInfo status)682 public void onServiceResolutionStatus(final ResolutionInfo status) { 683 mStateMachine.sendMessage( 684 MDNS_SERVICE_EVENT, status.result, status.id, status); 685 } 686 687 @Override onGettingServiceAddressStatus(final GetAddressInfo status)688 public void onGettingServiceAddressStatus(final GetAddressInfo status) { 689 mStateMachine.sendMessage( 690 MDNS_SERVICE_EVENT, status.result, status.id, status); 691 } 692 693 @Override getInterfaceVersion()694 public int getInterfaceVersion() throws RemoteException { 695 return this.VERSION; 696 } 697 698 @Override getInterfaceHash()699 public String getInterfaceHash() throws RemoteException { 700 return this.HASH; 701 } 702 } 703 704 @Override connect(INsdManagerCallback cb)705 public INsdServiceConnector connect(INsdManagerCallback cb) { 706 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); 707 final INsdServiceConnector connector = new NsdServiceConnector(); 708 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 709 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb))); 710 return connector; 711 } 712 713 private static class ListenerArgs { 714 public final NsdServiceConnector connector; 715 public final NsdServiceInfo serviceInfo; ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo)716 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) { 717 this.connector = connector; 718 this.serviceInfo = serviceInfo; 719 } 720 } 721 722 private class NsdServiceConnector extends INsdServiceConnector.Stub 723 implements IBinder.DeathRecipient { 724 @Override registerService(int listenerKey, NsdServiceInfo serviceInfo)725 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) { 726 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 727 NsdManager.REGISTER_SERVICE, 0, listenerKey, 728 new ListenerArgs(this, serviceInfo))); 729 } 730 731 @Override unregisterService(int listenerKey)732 public void unregisterService(int listenerKey) { 733 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 734 NsdManager.UNREGISTER_SERVICE, 0, listenerKey, 735 new ListenerArgs(this, null))); 736 } 737 738 @Override discoverServices(int listenerKey, NsdServiceInfo serviceInfo)739 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) { 740 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 741 NsdManager.DISCOVER_SERVICES, 0, listenerKey, 742 new ListenerArgs(this, serviceInfo))); 743 } 744 745 @Override stopDiscovery(int listenerKey)746 public void stopDiscovery(int listenerKey) { 747 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 748 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null))); 749 } 750 751 @Override resolveService(int listenerKey, NsdServiceInfo serviceInfo)752 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) { 753 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 754 NsdManager.RESOLVE_SERVICE, 0, listenerKey, 755 new ListenerArgs(this, serviceInfo))); 756 } 757 758 @Override startDaemon()759 public void startDaemon() { 760 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 761 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null))); 762 } 763 764 @Override binderDied()765 public void binderDied() { 766 mNsdStateMachine.sendMessage( 767 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this)); 768 } 769 } 770 sendNsdStateChangeBroadcast(boolean isEnabled)771 private void sendNsdStateChangeBroadcast(boolean isEnabled) { 772 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED); 773 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 774 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED; 775 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState); 776 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 777 } 778 getUniqueId()779 private int getUniqueId() { 780 if (++mUniqueId == INVALID_ID) return ++mUniqueId; 781 return mUniqueId; 782 } 783 registerService(int regId, NsdServiceInfo service)784 private boolean registerService(int regId, NsdServiceInfo service) { 785 if (DBG) { 786 Log.d(TAG, "registerService: " + regId + " " + service); 787 } 788 String name = service.getServiceName(); 789 String type = service.getServiceType(); 790 int port = service.getPort(); 791 byte[] textRecord = service.getTxtRecord(); 792 final int registerInterface = getNetworkInterfaceIndex(service); 793 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) { 794 Log.e(TAG, "Interface to register service on not found"); 795 return false; 796 } 797 return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface); 798 } 799 unregisterService(int regId)800 private boolean unregisterService(int regId) { 801 return mMDnsManager.stopOperation(regId); 802 } 803 discoverServices(int discoveryId, NsdServiceInfo serviceInfo)804 private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) { 805 final String type = serviceInfo.getServiceType(); 806 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo); 807 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) { 808 Log.e(TAG, "Interface to discover service on not found"); 809 return false; 810 } 811 return mMDnsManager.discover(discoveryId, type, discoverInterface); 812 } 813 stopServiceDiscovery(int discoveryId)814 private boolean stopServiceDiscovery(int discoveryId) { 815 return mMDnsManager.stopOperation(discoveryId); 816 } 817 resolveService(int resolveId, NsdServiceInfo service)818 private boolean resolveService(int resolveId, NsdServiceInfo service) { 819 final String name = service.getServiceName(); 820 final String type = service.getServiceType(); 821 final int resolveInterface = getNetworkInterfaceIndex(service); 822 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) { 823 Log.e(TAG, "Interface to resolve service on not found"); 824 return false; 825 } 826 return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface); 827 } 828 829 /** 830 * Guess the interface to use to resolve or discover a service on a specific network. 831 * 832 * This is an imperfect guess, as for example the network may be gone or not yet fully 833 * registered. This is fine as failing is correct if the network is gone, and a client 834 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also 835 * this is to support the legacy mdnsresponder implementation, which historically resolved 836 * services on an unspecified network. 837 */ getNetworkInterfaceIndex(NsdServiceInfo serviceInfo)838 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) { 839 final Network network = serviceInfo.getNetwork(); 840 if (network == null) { 841 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was 842 // provided by NsdService from discovery results, and the service was found on an 843 // interface that has no app-usable Network). 844 if (serviceInfo.getInterfaceIndex() != 0) { 845 return serviceInfo.getInterfaceIndex(); 846 } 847 return IFACE_IDX_ANY; 848 } 849 850 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 851 if (cm == null) { 852 Log.wtf(TAG, "No ConnectivityManager for resolveService"); 853 return IFACE_IDX_ANY; 854 } 855 final LinkProperties lp = cm.getLinkProperties(network); 856 if (lp == null) return IFACE_IDX_ANY; 857 858 // Only resolve on non-stacked interfaces 859 final NetworkInterface iface; 860 try { 861 iface = NetworkInterface.getByName(lp.getInterfaceName()); 862 } catch (SocketException e) { 863 Log.e(TAG, "Error querying interface", e); 864 return IFACE_IDX_ANY; 865 } 866 867 if (iface == null) { 868 Log.e(TAG, "Interface not found: " + lp.getInterfaceName()); 869 return IFACE_IDX_ANY; 870 } 871 872 return iface.getIndex(); 873 } 874 stopResolveService(int resolveId)875 private boolean stopResolveService(int resolveId) { 876 return mMDnsManager.stopOperation(resolveId); 877 } 878 getAddrInfo(int resolveId, String hostname, int interfaceIdx)879 private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) { 880 return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx); 881 } 882 stopGetAddrInfo(int resolveId)883 private boolean stopGetAddrInfo(int resolveId) { 884 return mMDnsManager.stopOperation(resolveId); 885 } 886 887 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)888 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 889 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 890 != PackageManager.PERMISSION_GRANTED) { 891 pw.println("Permission Denial: can't dump " + TAG 892 + " due to missing android.permission.DUMP permission"); 893 return; 894 } 895 896 for (ClientInfo client : mClients.values()) { 897 pw.println("Client Info"); 898 pw.println(client); 899 } 900 901 mNsdStateMachine.dump(fd, pw, args); 902 } 903 904 /* Information tracked per client */ 905 private class ClientInfo { 906 907 private static final int MAX_LIMIT = 10; 908 private final INsdManagerCallback mCb; 909 /* Remembers a resolved service until getaddrinfo completes */ 910 private NsdServiceInfo mResolvedService; 911 912 /* A map from client id to unique id sent to mDns */ 913 private final SparseIntArray mClientIds = new SparseIntArray(); 914 915 /* A map from client id to the type of the request we had received */ 916 private final SparseIntArray mClientRequests = new SparseIntArray(); 917 918 // The target SDK of this client < Build.VERSION_CODES.S 919 private boolean mIsLegacy = false; 920 ClientInfo(INsdManagerCallback cb)921 private ClientInfo(INsdManagerCallback cb) { 922 mCb = cb; 923 if (DBG) Log.d(TAG, "New client"); 924 } 925 926 @Override toString()927 public String toString() { 928 StringBuilder sb = new StringBuilder(); 929 sb.append("mResolvedService ").append(mResolvedService).append("\n"); 930 sb.append("mIsLegacy ").append(mIsLegacy).append("\n"); 931 for(int i = 0; i< mClientIds.size(); i++) { 932 int clientID = mClientIds.keyAt(i); 933 sb.append("clientId ").append(clientID). 934 append(" mDnsId ").append(mClientIds.valueAt(i)). 935 append(" type ").append(mClientRequests.get(clientID)).append("\n"); 936 } 937 return sb.toString(); 938 } 939 isLegacy()940 private boolean isLegacy() { 941 return mIsLegacy; 942 } 943 setLegacy()944 private void setLegacy() { 945 mIsLegacy = true; 946 } 947 948 // Remove any pending requests from the global map when we get rid of a client, 949 // and send cancellations to the daemon. expungeAllRequests()950 private void expungeAllRequests() { 951 int globalId, clientId, i; 952 // TODO: to keep handler responsive, do not clean all requests for that client at once. 953 for (i = 0; i < mClientIds.size(); i++) { 954 clientId = mClientIds.keyAt(i); 955 globalId = mClientIds.valueAt(i); 956 mIdToClientInfoMap.remove(globalId); 957 if (DBG) { 958 Log.d(TAG, "Terminating client-ID " + clientId 959 + " global-ID " + globalId + " type " + mClientRequests.get(clientId)); 960 } 961 switch (mClientRequests.get(clientId)) { 962 case NsdManager.DISCOVER_SERVICES: 963 stopServiceDiscovery(globalId); 964 break; 965 case NsdManager.RESOLVE_SERVICE: 966 stopResolveService(globalId); 967 break; 968 case NsdManager.REGISTER_SERVICE: 969 unregisterService(globalId); 970 break; 971 default: 972 break; 973 } 974 } 975 mClientIds.clear(); 976 mClientRequests.clear(); 977 } 978 979 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id, 980 // return the corresponding listener id. mDnsClient id is also called a global id. getClientId(final int globalId)981 private int getClientId(final int globalId) { 982 int idx = mClientIds.indexOfValue(globalId); 983 if (idx < 0) { 984 return idx; 985 } 986 return mClientIds.keyAt(idx); 987 } 988 onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info)989 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { 990 try { 991 mCb.onDiscoverServicesStarted(listenerKey, info); 992 } catch (RemoteException e) { 993 Log.e(TAG, "Error calling onDiscoverServicesStarted", e); 994 } 995 } 996 onDiscoverServicesFailed(int listenerKey, int error)997 void onDiscoverServicesFailed(int listenerKey, int error) { 998 try { 999 mCb.onDiscoverServicesFailed(listenerKey, error); 1000 } catch (RemoteException e) { 1001 Log.e(TAG, "Error calling onDiscoverServicesFailed", e); 1002 } 1003 } 1004 onServiceFound(int listenerKey, NsdServiceInfo info)1005 void onServiceFound(int listenerKey, NsdServiceInfo info) { 1006 try { 1007 mCb.onServiceFound(listenerKey, info); 1008 } catch (RemoteException e) { 1009 Log.e(TAG, "Error calling onServiceFound(", e); 1010 } 1011 } 1012 onServiceLost(int listenerKey, NsdServiceInfo info)1013 void onServiceLost(int listenerKey, NsdServiceInfo info) { 1014 try { 1015 mCb.onServiceLost(listenerKey, info); 1016 } catch (RemoteException e) { 1017 Log.e(TAG, "Error calling onServiceLost(", e); 1018 } 1019 } 1020 onStopDiscoveryFailed(int listenerKey, int error)1021 void onStopDiscoveryFailed(int listenerKey, int error) { 1022 try { 1023 mCb.onStopDiscoveryFailed(listenerKey, error); 1024 } catch (RemoteException e) { 1025 Log.e(TAG, "Error calling onStopDiscoveryFailed", e); 1026 } 1027 } 1028 onStopDiscoverySucceeded(int listenerKey)1029 void onStopDiscoverySucceeded(int listenerKey) { 1030 try { 1031 mCb.onStopDiscoverySucceeded(listenerKey); 1032 } catch (RemoteException e) { 1033 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e); 1034 } 1035 } 1036 onRegisterServiceFailed(int listenerKey, int error)1037 void onRegisterServiceFailed(int listenerKey, int error) { 1038 try { 1039 mCb.onRegisterServiceFailed(listenerKey, error); 1040 } catch (RemoteException e) { 1041 Log.e(TAG, "Error calling onRegisterServiceFailed", e); 1042 } 1043 } 1044 onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info)1045 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { 1046 try { 1047 mCb.onRegisterServiceSucceeded(listenerKey, info); 1048 } catch (RemoteException e) { 1049 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e); 1050 } 1051 } 1052 onUnregisterServiceFailed(int listenerKey, int error)1053 void onUnregisterServiceFailed(int listenerKey, int error) { 1054 try { 1055 mCb.onUnregisterServiceFailed(listenerKey, error); 1056 } catch (RemoteException e) { 1057 Log.e(TAG, "Error calling onUnregisterServiceFailed", e); 1058 } 1059 } 1060 onUnregisterServiceSucceeded(int listenerKey)1061 void onUnregisterServiceSucceeded(int listenerKey) { 1062 try { 1063 mCb.onUnregisterServiceSucceeded(listenerKey); 1064 } catch (RemoteException e) { 1065 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e); 1066 } 1067 } 1068 onResolveServiceFailed(int listenerKey, int error)1069 void onResolveServiceFailed(int listenerKey, int error) { 1070 try { 1071 mCb.onResolveServiceFailed(listenerKey, error); 1072 } catch (RemoteException e) { 1073 Log.e(TAG, "Error calling onResolveServiceFailed", e); 1074 } 1075 } 1076 onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info)1077 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { 1078 try { 1079 mCb.onResolveServiceSucceeded(listenerKey, info); 1080 } catch (RemoteException e) { 1081 Log.e(TAG, "Error calling onResolveServiceSucceeded", e); 1082 } 1083 } 1084 } 1085 } 1086