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_DISCOVERY_MANAGER_EVENT; 21 import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT; 22 import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED; 23 import static android.provider.DeviceConfig.NAMESPACE_TETHERING; 24 25 import static com.android.modules.utils.build.SdkLevel.isAtLeastU; 26 import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH; 27 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.net.ConnectivityManager; 33 import android.net.INetd; 34 import android.net.InetAddresses; 35 import android.net.LinkProperties; 36 import android.net.Network; 37 import android.net.mdns.aidl.DiscoveryInfo; 38 import android.net.mdns.aidl.GetAddressInfo; 39 import android.net.mdns.aidl.IMDnsEventListener; 40 import android.net.mdns.aidl.RegistrationInfo; 41 import android.net.mdns.aidl.ResolutionInfo; 42 import android.net.nsd.INsdManager; 43 import android.net.nsd.INsdManagerCallback; 44 import android.net.nsd.INsdServiceConnector; 45 import android.net.nsd.MDnsManager; 46 import android.net.nsd.NsdManager; 47 import android.net.nsd.NsdServiceInfo; 48 import android.os.Binder; 49 import android.os.Handler; 50 import android.os.HandlerThread; 51 import android.os.IBinder; 52 import android.os.Looper; 53 import android.os.Message; 54 import android.os.RemoteException; 55 import android.os.UserHandle; 56 import android.text.TextUtils; 57 import android.util.Log; 58 import android.util.Pair; 59 import android.util.SparseArray; 60 61 import com.android.internal.annotations.VisibleForTesting; 62 import com.android.internal.util.IndentingPrintWriter; 63 import com.android.internal.util.State; 64 import com.android.internal.util.StateMachine; 65 import com.android.net.module.util.DeviceConfigUtils; 66 import com.android.net.module.util.InetAddressUtils; 67 import com.android.net.module.util.PermissionUtils; 68 import com.android.net.module.util.SharedLog; 69 import com.android.server.connectivity.mdns.ExecutorProvider; 70 import com.android.server.connectivity.mdns.MdnsAdvertiser; 71 import com.android.server.connectivity.mdns.MdnsDiscoveryManager; 72 import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient; 73 import com.android.server.connectivity.mdns.MdnsSearchOptions; 74 import com.android.server.connectivity.mdns.MdnsServiceBrowserListener; 75 import com.android.server.connectivity.mdns.MdnsServiceInfo; 76 import com.android.server.connectivity.mdns.MdnsSocketProvider; 77 import com.android.server.connectivity.mdns.util.MdnsUtils; 78 79 import java.io.FileDescriptor; 80 import java.io.PrintWriter; 81 import java.net.Inet6Address; 82 import java.net.InetAddress; 83 import java.net.NetworkInterface; 84 import java.net.SocketException; 85 import java.net.UnknownHostException; 86 import java.util.ArrayList; 87 import java.util.Arrays; 88 import java.util.HashMap; 89 import java.util.List; 90 import java.util.Map; 91 import java.util.regex.Matcher; 92 import java.util.regex.Pattern; 93 94 /** 95 * Network Service Discovery Service handles remote service discovery operation requests by 96 * implementing the INsdManager interface. 97 * 98 * @hide 99 */ 100 public class NsdService extends INsdManager.Stub { 101 private static final String TAG = "NsdService"; 102 private static final String MDNS_TAG = "mDnsConnector"; 103 /** 104 * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder 105 * implementation. 106 */ 107 private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version"; 108 private static final String LOCAL_DOMAIN_NAME = "local"; 109 110 /** 111 * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder 112 * implementation. 113 */ 114 private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version"; 115 116 /** 117 * Comma-separated list of type:flag mappings indicating the flags to use to allowlist 118 * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type. 119 * 120 * For example _mytype._tcp.local and _othertype._tcp.local would be configured with: 121 * _mytype._tcp:mytype,_othertype._tcp.local:othertype 122 * 123 * In which case the flags: 124 * "mdns_discovery_manager_allowlist_mytype_version", 125 * "mdns_advertiser_allowlist_mytype_version", 126 * "mdns_discovery_manager_allowlist_othertype_version", 127 * "mdns_advertiser_allowlist_othertype_version" 128 * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will 129 * be read with 130 * {@link DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean)}. 131 * 132 * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX 133 * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX 134 * @see #MDNS_ALLOWLIST_FLAG_SUFFIX 135 */ 136 private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags"; 137 138 private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX = 139 "mdns_discovery_manager_allowlist_"; 140 private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX = 141 "mdns_advertiser_allowlist_"; 142 private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version"; 143 144 public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 145 private static final long CLEANUP_DELAY_MS = 10000; 146 private static final int IFACE_IDX_ANY = 0; 147 private static final SharedLog LOGGER = new SharedLog("serviceDiscovery"); 148 149 private final Context mContext; 150 private final NsdStateMachine mNsdStateMachine; 151 private final MDnsManager mMDnsManager; 152 private final MDnsEventCallback mMDnsEventCallback; 153 @NonNull 154 private final Dependencies mDeps; 155 @NonNull 156 private final MdnsMultinetworkSocketClient mMdnsSocketClient; 157 @NonNull 158 private final MdnsDiscoveryManager mMdnsDiscoveryManager; 159 @NonNull 160 private final MdnsSocketProvider mMdnsSocketProvider; 161 @NonNull 162 private final MdnsAdvertiser mAdvertiser; 163 private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG); 164 // WARNING : Accessing these values in any thread is not safe, it must only be changed in the 165 // state machine thread. If change this outside state machine, it will need to introduce 166 // synchronization. 167 private boolean mIsDaemonStarted = false; 168 private boolean mIsMonitoringSocketsStarted = false; 169 170 /** 171 * Clients receiving asynchronous messages 172 */ 173 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>(); 174 175 /* A map from unique id to client info */ 176 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>(); 177 178 private final long mCleanupDelayMs; 179 180 private static final int INVALID_ID = 0; 181 private int mUniqueId = 1; 182 // The count of the connected legacy clients. 183 private int mLegacyClientCount = 0; 184 // The number of client that ever connected. 185 private int mClientNumberId = 1; 186 187 private static class MdnsListener implements MdnsServiceBrowserListener { 188 protected final int mClientId; 189 protected final int mTransactionId; 190 @NonNull 191 protected final NsdServiceInfo mReqServiceInfo; 192 @NonNull 193 protected final String mListenedServiceType; 194 MdnsListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenedServiceType)195 MdnsListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, 196 @NonNull String listenedServiceType) { 197 mClientId = clientId; 198 mTransactionId = transactionId; 199 mReqServiceInfo = reqServiceInfo; 200 mListenedServiceType = listenedServiceType; 201 } 202 203 @NonNull getListenedServiceType()204 public String getListenedServiceType() { 205 return mListenedServiceType; 206 } 207 208 @Override onServiceFound(@onNull MdnsServiceInfo serviceInfo)209 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) { } 210 211 @Override onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)212 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { } 213 214 @Override onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)215 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { } 216 217 @Override onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo)218 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) { } 219 220 @Override onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)221 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { } 222 223 @Override onSearchStoppedWithError(int error)224 public void onSearchStoppedWithError(int error) { } 225 226 @Override onSearchFailedToStart()227 public void onSearchFailedToStart() { } 228 229 @Override onDiscoveryQuerySent(@onNull List<String> subtypes, int transactionId)230 public void onDiscoveryQuerySent(@NonNull List<String> subtypes, int transactionId) { } 231 232 @Override onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode)233 public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { } 234 } 235 236 private class DiscoveryListener extends MdnsListener { 237 DiscoveryListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType)238 DiscoveryListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, 239 @NonNull String listenServiceType) { 240 super(clientId, transactionId, reqServiceInfo, listenServiceType); 241 } 242 243 @Override onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo)244 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) { 245 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 246 NsdManager.SERVICE_FOUND, 247 new MdnsEvent(mClientId, serviceInfo)); 248 } 249 250 @Override onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)251 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { 252 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 253 NsdManager.SERVICE_LOST, 254 new MdnsEvent(mClientId, serviceInfo)); 255 } 256 } 257 258 private class ResolutionListener extends MdnsListener { 259 ResolutionListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType)260 ResolutionListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, 261 @NonNull String listenServiceType) { 262 super(clientId, transactionId, reqServiceInfo, listenServiceType); 263 } 264 265 @Override onServiceFound(MdnsServiceInfo serviceInfo)266 public void onServiceFound(MdnsServiceInfo serviceInfo) { 267 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 268 NsdManager.RESOLVE_SERVICE_SUCCEEDED, 269 new MdnsEvent(mClientId, serviceInfo)); 270 } 271 } 272 273 private class ServiceInfoListener extends MdnsListener { 274 ServiceInfoListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType)275 ServiceInfoListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo, 276 @NonNull String listenServiceType) { 277 super(clientId, transactionId, reqServiceInfo, listenServiceType); 278 } 279 280 @Override onServiceFound(@onNull MdnsServiceInfo serviceInfo)281 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) { 282 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 283 NsdManager.SERVICE_UPDATED, 284 new MdnsEvent(mClientId, serviceInfo)); 285 } 286 287 @Override onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)288 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { 289 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 290 NsdManager.SERVICE_UPDATED, 291 new MdnsEvent(mClientId, serviceInfo)); 292 } 293 294 @Override onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)295 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { 296 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 297 NsdManager.SERVICE_UPDATED_LOST, 298 new MdnsEvent(mClientId, serviceInfo)); 299 } 300 } 301 302 /** 303 * Data class of mdns service callback information. 304 */ 305 private static class MdnsEvent { 306 final int mClientId; 307 @NonNull 308 final MdnsServiceInfo mMdnsServiceInfo; 309 MdnsEvent(int clientId, @NonNull MdnsServiceInfo mdnsServiceInfo)310 MdnsEvent(int clientId, @NonNull MdnsServiceInfo mdnsServiceInfo) { 311 mClientId = clientId; 312 mMdnsServiceInfo = mdnsServiceInfo; 313 } 314 } 315 316 private class NsdStateMachine extends StateMachine { 317 318 private final DefaultState mDefaultState = new DefaultState(); 319 private final EnabledState mEnabledState = new EnabledState(); 320 321 @Override getWhatToString(int what)322 protected String getWhatToString(int what) { 323 return NsdManager.nameOf(what); 324 } 325 maybeStartDaemon()326 private void maybeStartDaemon() { 327 if (mIsDaemonStarted) { 328 if (DBG) Log.d(TAG, "Daemon is already started."); 329 return; 330 } 331 mMDnsManager.registerEventListener(mMDnsEventCallback); 332 mMDnsManager.startDaemon(); 333 mIsDaemonStarted = true; 334 maybeScheduleStop(); 335 mServiceLogs.log("Start mdns_responder daemon"); 336 } 337 maybeStopDaemon()338 private void maybeStopDaemon() { 339 if (!mIsDaemonStarted) { 340 if (DBG) Log.d(TAG, "Daemon has not been started."); 341 return; 342 } 343 mMDnsManager.unregisterEventListener(mMDnsEventCallback); 344 mMDnsManager.stopDaemon(); 345 mIsDaemonStarted = false; 346 mServiceLogs.log("Stop mdns_responder daemon"); 347 } 348 isAnyRequestActive()349 private boolean isAnyRequestActive() { 350 return mIdToClientInfoMap.size() != 0; 351 } 352 scheduleStop()353 private void scheduleStop() { 354 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs); 355 } maybeScheduleStop()356 private void maybeScheduleStop() { 357 // The native daemon should stay alive and can't be cleanup 358 // if any legacy client connected. 359 if (!isAnyRequestActive() && mLegacyClientCount == 0) { 360 scheduleStop(); 361 } 362 } 363 cancelStop()364 private void cancelStop() { 365 this.removeMessages(NsdManager.DAEMON_CLEANUP); 366 } 367 maybeStartMonitoringSockets()368 private void maybeStartMonitoringSockets() { 369 if (mIsMonitoringSocketsStarted) { 370 if (DBG) Log.d(TAG, "Socket monitoring is already started."); 371 return; 372 } 373 374 mMdnsSocketProvider.startMonitoringSockets(); 375 mIsMonitoringSocketsStarted = true; 376 } 377 maybeStopMonitoringSocketsIfNoActiveRequest()378 private void maybeStopMonitoringSocketsIfNoActiveRequest() { 379 if (!mIsMonitoringSocketsStarted) return; 380 if (isAnyRequestActive()) return; 381 382 mMdnsSocketProvider.requestStopWhenInactive(); 383 mIsMonitoringSocketsStarted = false; 384 } 385 NsdStateMachine(String name, Handler handler)386 NsdStateMachine(String name, Handler handler) { 387 super(name, handler); 388 addState(mDefaultState); 389 addState(mEnabledState, mDefaultState); 390 State initialState = mEnabledState; 391 setInitialState(initialState); 392 setLogRecSize(25); 393 } 394 395 class DefaultState extends State { 396 @Override processMessage(Message msg)397 public boolean processMessage(Message msg) { 398 final ClientInfo cInfo; 399 final int clientId = msg.arg2; 400 switch (msg.what) { 401 case NsdManager.REGISTER_CLIENT: 402 final ConnectorArgs arg = (ConnectorArgs) msg.obj; 403 final INsdManagerCallback cb = arg.callback; 404 try { 405 cb.asBinder().linkToDeath(arg.connector, 0); 406 final String tag = "Client" + arg.uid + "-" + mClientNumberId++; 407 cInfo = new ClientInfo(cb, arg.useJavaBackend, 408 mServiceLogs.forSubComponent(tag)); 409 mClients.put(arg.connector, cInfo); 410 } catch (RemoteException e) { 411 Log.w(TAG, "Client " + clientId + " has already died"); 412 } 413 break; 414 case NsdManager.UNREGISTER_CLIENT: 415 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj; 416 cInfo = mClients.remove(connector); 417 if (cInfo != null) { 418 cInfo.expungeAllRequests(); 419 if (cInfo.isPreSClient()) { 420 mLegacyClientCount -= 1; 421 } 422 } 423 maybeStopMonitoringSocketsIfNoActiveRequest(); 424 maybeScheduleStop(); 425 break; 426 case NsdManager.DISCOVER_SERVICES: 427 cInfo = getClientInfoForReply(msg); 428 if (cInfo != null) { 429 cInfo.onDiscoverServicesFailed( 430 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 431 } 432 break; 433 case NsdManager.STOP_DISCOVERY: 434 cInfo = getClientInfoForReply(msg); 435 if (cInfo != null) { 436 cInfo.onStopDiscoveryFailed( 437 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 438 } 439 break; 440 case NsdManager.REGISTER_SERVICE: 441 cInfo = getClientInfoForReply(msg); 442 if (cInfo != null) { 443 cInfo.onRegisterServiceFailed( 444 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 445 } 446 break; 447 case NsdManager.UNREGISTER_SERVICE: 448 cInfo = getClientInfoForReply(msg); 449 if (cInfo != null) { 450 cInfo.onUnregisterServiceFailed( 451 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 452 } 453 break; 454 case NsdManager.RESOLVE_SERVICE: 455 cInfo = getClientInfoForReply(msg); 456 if (cInfo != null) { 457 cInfo.onResolveServiceFailed( 458 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 459 } 460 break; 461 case NsdManager.STOP_RESOLUTION: 462 cInfo = getClientInfoForReply(msg); 463 if (cInfo != null) { 464 cInfo.onStopResolutionFailed( 465 clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING); 466 } 467 break; 468 case NsdManager.REGISTER_SERVICE_CALLBACK: 469 cInfo = getClientInfoForReply(msg); 470 if (cInfo != null) { 471 cInfo.onServiceInfoCallbackRegistrationFailed( 472 clientId, NsdManager.FAILURE_BAD_PARAMETERS); 473 } 474 break; 475 case NsdManager.DAEMON_CLEANUP: 476 maybeStopDaemon(); 477 break; 478 // This event should be only sent by the legacy (target SDK < S) clients. 479 // Mark the sending client as legacy. 480 case NsdManager.DAEMON_STARTUP: 481 cInfo = getClientInfoForReply(msg); 482 if (cInfo != null) { 483 cancelStop(); 484 cInfo.setPreSClient(); 485 mLegacyClientCount += 1; 486 maybeStartDaemon(); 487 } 488 break; 489 default: 490 Log.e(TAG, "Unhandled " + msg); 491 return NOT_HANDLED; 492 } 493 return HANDLED; 494 } 495 getClientInfoForReply(Message msg)496 private ClientInfo getClientInfoForReply(Message msg) { 497 final ListenerArgs args = (ListenerArgs) msg.obj; 498 return mClients.get(args.connector); 499 } 500 } 501 502 class EnabledState extends State { 503 @Override enter()504 public void enter() { 505 sendNsdStateChangeBroadcast(true); 506 } 507 508 @Override exit()509 public void exit() { 510 // TODO: it is incorrect to stop the daemon without expunging all requests 511 // and sending error callbacks to clients. 512 scheduleStop(); 513 } 514 requestLimitReached(ClientInfo clientInfo)515 private boolean requestLimitReached(ClientInfo clientInfo) { 516 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) { 517 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo); 518 return true; 519 } 520 return false; 521 } 522 storeLegacyRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what)523 private void storeLegacyRequestMap(int clientId, int globalId, ClientInfo clientInfo, 524 int what) { 525 clientInfo.mClientRequests.put(clientId, new LegacyClientRequest(globalId, what)); 526 mIdToClientInfoMap.put(globalId, clientInfo); 527 // Remove the cleanup event because here comes a new request. 528 cancelStop(); 529 } 530 storeAdvertiserRequestMap(int clientId, int globalId, ClientInfo clientInfo)531 private void storeAdvertiserRequestMap(int clientId, int globalId, 532 ClientInfo clientInfo) { 533 clientInfo.mClientRequests.put(clientId, new AdvertiserClientRequest(globalId)); 534 mIdToClientInfoMap.put(globalId, clientInfo); 535 } 536 removeRequestMap(int clientId, int globalId, ClientInfo clientInfo)537 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) { 538 final ClientRequest existing = clientInfo.mClientRequests.get(clientId); 539 if (existing == null) return; 540 clientInfo.mClientRequests.remove(clientId); 541 mIdToClientInfoMap.remove(globalId); 542 543 if (existing instanceof LegacyClientRequest) { 544 maybeScheduleStop(); 545 } else { 546 maybeStopMonitoringSocketsIfNoActiveRequest(); 547 } 548 } 549 storeDiscoveryManagerRequestMap(int clientId, int globalId, MdnsListener listener, ClientInfo clientInfo)550 private void storeDiscoveryManagerRequestMap(int clientId, int globalId, 551 MdnsListener listener, ClientInfo clientInfo) { 552 clientInfo.mClientRequests.put(clientId, 553 new DiscoveryManagerRequest(globalId, listener)); 554 mIdToClientInfoMap.put(globalId, clientInfo); 555 } 556 557 /** 558 * Truncate a service name to up to 63 UTF-8 bytes. 559 * 560 * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating 561 * names used in registerService follows historical behavior (see mdnsresponder 562 * handle_regservice_request). 563 */ 564 @NonNull truncateServiceName(@onNull String originalName)565 private String truncateServiceName(@NonNull String originalName) { 566 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH); 567 } 568 stopDiscoveryManagerRequest(ClientRequest request, int clientId, int id, ClientInfo clientInfo)569 private void stopDiscoveryManagerRequest(ClientRequest request, int clientId, int id, 570 ClientInfo clientInfo) { 571 clientInfo.unregisterMdnsListenerFromRequest(request); 572 removeRequestMap(clientId, id, clientInfo); 573 } 574 575 @Override processMessage(Message msg)576 public boolean processMessage(Message msg) { 577 final ClientInfo clientInfo; 578 final int id; 579 final int clientId = msg.arg2; 580 final ListenerArgs args; 581 switch (msg.what) { 582 case NsdManager.DISCOVER_SERVICES: { 583 if (DBG) Log.d(TAG, "Discover services"); 584 args = (ListenerArgs) msg.obj; 585 clientInfo = mClients.get(args.connector); 586 // If the binder death notification for a INsdManagerCallback was received 587 // before any calls are received by NsdService, the clientInfo would be 588 // cleared and cause NPE. Add a null check here to prevent this corner case. 589 if (clientInfo == null) { 590 Log.e(TAG, "Unknown connector in discovery"); 591 break; 592 } 593 594 if (requestLimitReached(clientInfo)) { 595 clientInfo.onDiscoverServicesFailed( 596 clientId, NsdManager.FAILURE_MAX_LIMIT); 597 break; 598 } 599 600 final NsdServiceInfo info = args.serviceInfo; 601 id = getUniqueId(); 602 final Pair<String, String> typeAndSubtype = 603 parseTypeAndSubtype(info.getServiceType()); 604 final String serviceType = typeAndSubtype == null 605 ? null : typeAndSubtype.first; 606 if (clientInfo.mUseJavaBackend 607 || mDeps.isMdnsDiscoveryManagerEnabled(mContext) 608 || useDiscoveryManagerForType(serviceType)) { 609 if (serviceType == null) { 610 clientInfo.onDiscoverServicesFailed(clientId, 611 NsdManager.FAILURE_INTERNAL_ERROR); 612 break; 613 } 614 615 final String listenServiceType = serviceType + ".local"; 616 maybeStartMonitoringSockets(); 617 final MdnsListener listener = 618 new DiscoveryListener(clientId, id, info, listenServiceType); 619 final MdnsSearchOptions.Builder optionsBuilder = 620 MdnsSearchOptions.newBuilder() 621 .setNetwork(info.getNetwork()) 622 .setRemoveExpiredService(true) 623 .setIsPassiveMode(true); 624 if (typeAndSubtype.second != null) { 625 // The parsing ensures subtype starts with an underscore. 626 // MdnsSearchOptions expects the underscore to not be present. 627 optionsBuilder.addSubtype(typeAndSubtype.second.substring(1)); 628 } 629 mMdnsDiscoveryManager.registerListener( 630 listenServiceType, listener, optionsBuilder.build()); 631 storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo); 632 clientInfo.onDiscoverServicesStarted(clientId, info); 633 clientInfo.log("Register a DiscoveryListener " + id 634 + " for service type:" + listenServiceType); 635 } else { 636 maybeStartDaemon(); 637 if (discoverServices(id, info)) { 638 if (DBG) { 639 Log.d(TAG, "Discover " + msg.arg2 + " " + id 640 + info.getServiceType()); 641 } 642 storeLegacyRequestMap(clientId, id, clientInfo, msg.what); 643 clientInfo.onDiscoverServicesStarted(clientId, info); 644 } else { 645 stopServiceDiscovery(id); 646 clientInfo.onDiscoverServicesFailed(clientId, 647 NsdManager.FAILURE_INTERNAL_ERROR); 648 } 649 } 650 break; 651 } 652 case NsdManager.STOP_DISCOVERY: { 653 if (DBG) Log.d(TAG, "Stop service discovery"); 654 args = (ListenerArgs) msg.obj; 655 clientInfo = mClients.get(args.connector); 656 // If the binder death notification for a INsdManagerCallback was received 657 // before any calls are received by NsdService, the clientInfo would be 658 // cleared and cause NPE. Add a null check here to prevent this corner case. 659 if (clientInfo == null) { 660 Log.e(TAG, "Unknown connector in stop discovery"); 661 break; 662 } 663 664 final ClientRequest request = clientInfo.mClientRequests.get(clientId); 665 if (request == null) { 666 Log.e(TAG, "Unknown client request in STOP_DISCOVERY"); 667 break; 668 } 669 id = request.mGlobalId; 670 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this 671 // point, so this needs to check the type of the original request to 672 // unregister instead of looking at the flag value. 673 if (request instanceof DiscoveryManagerRequest) { 674 stopDiscoveryManagerRequest(request, clientId, id, clientInfo); 675 clientInfo.onStopDiscoverySucceeded(clientId); 676 clientInfo.log("Unregister the DiscoveryListener " + id); 677 } else { 678 removeRequestMap(clientId, id, clientInfo); 679 if (stopServiceDiscovery(id)) { 680 clientInfo.onStopDiscoverySucceeded(clientId); 681 } else { 682 clientInfo.onStopDiscoveryFailed( 683 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 684 } 685 } 686 break; 687 } 688 case NsdManager.REGISTER_SERVICE: { 689 if (DBG) Log.d(TAG, "Register service"); 690 args = (ListenerArgs) msg.obj; 691 clientInfo = mClients.get(args.connector); 692 // If the binder death notification for a INsdManagerCallback was received 693 // before any calls are received by NsdService, the clientInfo would be 694 // cleared and cause NPE. Add a null check here to prevent this corner case. 695 if (clientInfo == null) { 696 Log.e(TAG, "Unknown connector in registration"); 697 break; 698 } 699 700 if (requestLimitReached(clientInfo)) { 701 clientInfo.onRegisterServiceFailed( 702 clientId, NsdManager.FAILURE_MAX_LIMIT); 703 break; 704 } 705 706 id = getUniqueId(); 707 final NsdServiceInfo serviceInfo = args.serviceInfo; 708 final String serviceType = serviceInfo.getServiceType(); 709 final Pair<String, String> typeSubtype = parseTypeAndSubtype(serviceType); 710 final String registerServiceType = typeSubtype == null 711 ? null : typeSubtype.first; 712 if (clientInfo.mUseJavaBackend 713 || mDeps.isMdnsAdvertiserEnabled(mContext) 714 || useAdvertiserForType(registerServiceType)) { 715 if (registerServiceType == null) { 716 Log.e(TAG, "Invalid service type: " + serviceType); 717 clientInfo.onRegisterServiceFailed(clientId, 718 NsdManager.FAILURE_INTERNAL_ERROR); 719 break; 720 } 721 serviceInfo.setServiceType(registerServiceType); 722 serviceInfo.setServiceName(truncateServiceName( 723 serviceInfo.getServiceName())); 724 725 maybeStartMonitoringSockets(); 726 // TODO: pass in the subtype as well. Including the subtype in the 727 // service type would generate service instance names like 728 // Name._subtype._sub._type._tcp, which is incorrect 729 // (it should be Name._type._tcp). 730 mAdvertiser.addService(id, serviceInfo, typeSubtype.second); 731 storeAdvertiserRequestMap(clientId, id, clientInfo); 732 } else { 733 maybeStartDaemon(); 734 if (registerService(id, serviceInfo)) { 735 if (DBG) Log.d(TAG, "Register " + clientId + " " + id); 736 storeLegacyRequestMap(clientId, id, clientInfo, msg.what); 737 // Return success after mDns reports success 738 } else { 739 unregisterService(id); 740 clientInfo.onRegisterServiceFailed( 741 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 742 } 743 744 } 745 break; 746 } 747 case NsdManager.UNREGISTER_SERVICE: { 748 if (DBG) Log.d(TAG, "unregister service"); 749 args = (ListenerArgs) msg.obj; 750 clientInfo = mClients.get(args.connector); 751 // If the binder death notification for a INsdManagerCallback was received 752 // before any calls are received by NsdService, the clientInfo would be 753 // cleared and cause NPE. Add a null check here to prevent this corner case. 754 if (clientInfo == null) { 755 Log.e(TAG, "Unknown connector in unregistration"); 756 break; 757 } 758 final ClientRequest request = clientInfo.mClientRequests.get(clientId); 759 if (request == null) { 760 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE"); 761 break; 762 } 763 id = request.mGlobalId; 764 removeRequestMap(clientId, id, clientInfo); 765 766 // Note isMdnsAdvertiserEnabled may have changed to false at this point, 767 // so this needs to check the type of the original request to unregister 768 // instead of looking at the flag value. 769 if (request instanceof AdvertiserClientRequest) { 770 mAdvertiser.removeService(id); 771 clientInfo.onUnregisterServiceSucceeded(clientId); 772 } else { 773 if (unregisterService(id)) { 774 clientInfo.onUnregisterServiceSucceeded(clientId); 775 } else { 776 clientInfo.onUnregisterServiceFailed( 777 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 778 } 779 } 780 break; 781 } 782 case NsdManager.RESOLVE_SERVICE: { 783 if (DBG) Log.d(TAG, "Resolve service"); 784 args = (ListenerArgs) msg.obj; 785 clientInfo = mClients.get(args.connector); 786 // If the binder death notification for a INsdManagerCallback was received 787 // before any calls are received by NsdService, the clientInfo would be 788 // cleared and cause NPE. Add a null check here to prevent this corner case. 789 if (clientInfo == null) { 790 Log.e(TAG, "Unknown connector in resolution"); 791 break; 792 } 793 794 final NsdServiceInfo info = args.serviceInfo; 795 id = getUniqueId(); 796 final Pair<String, String> typeSubtype = 797 parseTypeAndSubtype(info.getServiceType()); 798 final String serviceType = typeSubtype == null 799 ? null : typeSubtype.first; 800 if (clientInfo.mUseJavaBackend 801 || mDeps.isMdnsDiscoveryManagerEnabled(mContext) 802 || useDiscoveryManagerForType(serviceType)) { 803 if (serviceType == null) { 804 clientInfo.onResolveServiceFailed(clientId, 805 NsdManager.FAILURE_INTERNAL_ERROR); 806 break; 807 } 808 final String resolveServiceType = serviceType + ".local"; 809 810 maybeStartMonitoringSockets(); 811 final MdnsListener listener = 812 new ResolutionListener(clientId, id, info, resolveServiceType); 813 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() 814 .setNetwork(info.getNetwork()) 815 .setIsPassiveMode(true) 816 .setResolveInstanceName(info.getServiceName()) 817 .setRemoveExpiredService(true) 818 .build(); 819 mMdnsDiscoveryManager.registerListener( 820 resolveServiceType, listener, options); 821 storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo); 822 clientInfo.log("Register a ResolutionListener " + id 823 + " for service type:" + resolveServiceType); 824 } else { 825 if (clientInfo.mResolvedService != null) { 826 clientInfo.onResolveServiceFailed( 827 clientId, NsdManager.FAILURE_ALREADY_ACTIVE); 828 break; 829 } 830 831 maybeStartDaemon(); 832 if (resolveService(id, info)) { 833 clientInfo.mResolvedService = new NsdServiceInfo(); 834 storeLegacyRequestMap(clientId, id, clientInfo, msg.what); 835 } else { 836 clientInfo.onResolveServiceFailed( 837 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 838 } 839 } 840 break; 841 } 842 case NsdManager.STOP_RESOLUTION: { 843 if (DBG) Log.d(TAG, "Stop service resolution"); 844 args = (ListenerArgs) msg.obj; 845 clientInfo = mClients.get(args.connector); 846 // If the binder death notification for a INsdManagerCallback was received 847 // before any calls are received by NsdService, the clientInfo would be 848 // cleared and cause NPE. Add a null check here to prevent this corner case. 849 if (clientInfo == null) { 850 Log.e(TAG, "Unknown connector in stop resolution"); 851 break; 852 } 853 854 final ClientRequest request = clientInfo.mClientRequests.get(clientId); 855 if (request == null) { 856 Log.e(TAG, "Unknown client request in STOP_RESOLUTION"); 857 break; 858 } 859 id = request.mGlobalId; 860 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this 861 // point, so this needs to check the type of the original request to 862 // unregister instead of looking at the flag value. 863 if (request instanceof DiscoveryManagerRequest) { 864 stopDiscoveryManagerRequest(request, clientId, id, clientInfo); 865 clientInfo.onStopResolutionSucceeded(clientId); 866 clientInfo.log("Unregister the ResolutionListener " + id); 867 } else { 868 removeRequestMap(clientId, id, clientInfo); 869 if (stopResolveService(id)) { 870 clientInfo.onStopResolutionSucceeded(clientId); 871 } else { 872 clientInfo.onStopResolutionFailed( 873 clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING); 874 } 875 clientInfo.mResolvedService = null; 876 } 877 break; 878 } 879 case NsdManager.REGISTER_SERVICE_CALLBACK: { 880 if (DBG) Log.d(TAG, "Register a service callback"); 881 args = (ListenerArgs) msg.obj; 882 clientInfo = mClients.get(args.connector); 883 // If the binder death notification for a INsdManagerCallback was received 884 // before any calls are received by NsdService, the clientInfo would be 885 // cleared and cause NPE. Add a null check here to prevent this corner case. 886 if (clientInfo == null) { 887 Log.e(TAG, "Unknown connector in callback registration"); 888 break; 889 } 890 891 final NsdServiceInfo info = args.serviceInfo; 892 id = getUniqueId(); 893 final Pair<String, String> typeAndSubtype = 894 parseTypeAndSubtype(info.getServiceType()); 895 final String serviceType = typeAndSubtype == null 896 ? null : typeAndSubtype.first; 897 if (serviceType == null) { 898 clientInfo.onServiceInfoCallbackRegistrationFailed(clientId, 899 NsdManager.FAILURE_BAD_PARAMETERS); 900 break; 901 } 902 final String resolveServiceType = serviceType + ".local"; 903 904 maybeStartMonitoringSockets(); 905 final MdnsListener listener = 906 new ServiceInfoListener(clientId, id, info, resolveServiceType); 907 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() 908 .setNetwork(info.getNetwork()) 909 .setIsPassiveMode(true) 910 .setResolveInstanceName(info.getServiceName()) 911 .setRemoveExpiredService(true) 912 .build(); 913 mMdnsDiscoveryManager.registerListener( 914 resolveServiceType, listener, options); 915 storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo); 916 clientInfo.log("Register a ServiceInfoListener " + id 917 + " for service type:" + resolveServiceType); 918 break; 919 } 920 case NsdManager.UNREGISTER_SERVICE_CALLBACK: { 921 if (DBG) Log.d(TAG, "Unregister a service callback"); 922 args = (ListenerArgs) msg.obj; 923 clientInfo = mClients.get(args.connector); 924 // If the binder death notification for a INsdManagerCallback was received 925 // before any calls are received by NsdService, the clientInfo would be 926 // cleared and cause NPE. Add a null check here to prevent this corner case. 927 if (clientInfo == null) { 928 Log.e(TAG, "Unknown connector in callback unregistration"); 929 break; 930 } 931 932 final ClientRequest request = clientInfo.mClientRequests.get(clientId); 933 if (request == null) { 934 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK"); 935 break; 936 } 937 id = request.mGlobalId; 938 if (request instanceof DiscoveryManagerRequest) { 939 stopDiscoveryManagerRequest(request, clientId, id, clientInfo); 940 clientInfo.onServiceInfoCallbackUnregistered(clientId); 941 clientInfo.log("Unregister the ServiceInfoListener " + id); 942 } else { 943 loge("Unregister failed with non-DiscoveryManagerRequest."); 944 } 945 break; 946 } 947 case MDNS_SERVICE_EVENT: 948 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) { 949 return NOT_HANDLED; 950 } 951 break; 952 case MDNS_DISCOVERY_MANAGER_EVENT: 953 if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) { 954 return NOT_HANDLED; 955 } 956 break; 957 default: 958 return NOT_HANDLED; 959 } 960 return HANDLED; 961 } 962 handleMDnsServiceEvent(int code, int id, Object obj)963 private boolean handleMDnsServiceEvent(int code, int id, Object obj) { 964 NsdServiceInfo servInfo; 965 ClientInfo clientInfo = mIdToClientInfoMap.get(id); 966 if (clientInfo == null) { 967 Log.e(TAG, String.format("id %d for %d has no client mapping", id, code)); 968 return false; 969 } 970 971 /* This goes in response as msg.arg2 */ 972 int clientId = clientInfo.getClientId(id); 973 if (clientId < 0) { 974 // This can happen because of race conditions. For example, 975 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY, 976 // and we may get in this situation. 977 Log.d(TAG, String.format("%d for listener id %d that is no longer active", 978 code, id)); 979 return false; 980 } 981 if (DBG) { 982 Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id)); 983 } 984 switch (code) { 985 case IMDnsEventListener.SERVICE_FOUND: { 986 final DiscoveryInfo info = (DiscoveryInfo) obj; 987 final String name = info.serviceName; 988 final String type = info.registrationType; 989 servInfo = new NsdServiceInfo(name, type); 990 final int foundNetId = info.netId; 991 if (foundNetId == 0L) { 992 // Ignore services that do not have a Network: they are not usable 993 // by apps, as they would need privileged permissions to use 994 // interfaces that do not have an associated Network. 995 break; 996 } 997 if (foundNetId == INetd.DUMMY_NET_ID) { 998 // Ignore services on the dummy0 interface: they are only seen when 999 // discovering locally advertised services, and are not reachable 1000 // through that interface. 1001 break; 1002 } 1003 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx); 1004 clientInfo.onServiceFound(clientId, servInfo); 1005 break; 1006 } 1007 case IMDnsEventListener.SERVICE_LOST: { 1008 final DiscoveryInfo info = (DiscoveryInfo) obj; 1009 final String name = info.serviceName; 1010 final String type = info.registrationType; 1011 final int lostNetId = info.netId; 1012 servInfo = new NsdServiceInfo(name, type); 1013 // The network could be set to null (netId 0) if it was torn down when the 1014 // service is lost 1015 // TODO: avoid returning null in that case, possibly by remembering 1016 // found services on the same interface index and their network at the time 1017 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx); 1018 clientInfo.onServiceLost(clientId, servInfo); 1019 break; 1020 } 1021 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED: 1022 clientInfo.onDiscoverServicesFailed( 1023 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 1024 break; 1025 case IMDnsEventListener.SERVICE_REGISTERED: { 1026 final RegistrationInfo info = (RegistrationInfo) obj; 1027 final String name = info.serviceName; 1028 servInfo = new NsdServiceInfo(name, null /* serviceType */); 1029 clientInfo.onRegisterServiceSucceeded(clientId, servInfo); 1030 break; 1031 } 1032 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED: 1033 clientInfo.onRegisterServiceFailed( 1034 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 1035 break; 1036 case IMDnsEventListener.SERVICE_RESOLVED: { 1037 final ResolutionInfo info = (ResolutionInfo) obj; 1038 int index = 0; 1039 final String fullName = info.serviceFullName; 1040 while (index < fullName.length() && fullName.charAt(index) != '.') { 1041 if (fullName.charAt(index) == '\\') { 1042 ++index; 1043 } 1044 ++index; 1045 } 1046 if (index >= fullName.length()) { 1047 Log.e(TAG, "Invalid service found " + fullName); 1048 break; 1049 } 1050 1051 String name = unescape(fullName.substring(0, index)); 1052 String rest = fullName.substring(index); 1053 String type = rest.replace(".local.", ""); 1054 1055 final NsdServiceInfo serviceInfo = clientInfo.mResolvedService; 1056 serviceInfo.setServiceName(name); 1057 serviceInfo.setServiceType(type); 1058 serviceInfo.setPort(info.port); 1059 serviceInfo.setTxtRecords(info.txtRecord); 1060 // Network will be added after SERVICE_GET_ADDR_SUCCESS 1061 1062 stopResolveService(id); 1063 removeRequestMap(clientId, id, clientInfo); 1064 1065 final int id2 = getUniqueId(); 1066 if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) { 1067 storeLegacyRequestMap(clientId, id2, clientInfo, 1068 NsdManager.RESOLVE_SERVICE); 1069 } else { 1070 clientInfo.onResolveServiceFailed( 1071 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 1072 clientInfo.mResolvedService = null; 1073 } 1074 break; 1075 } 1076 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED: 1077 /* NNN resolveId errorCode */ 1078 stopResolveService(id); 1079 removeRequestMap(clientId, id, clientInfo); 1080 clientInfo.onResolveServiceFailed( 1081 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 1082 clientInfo.mResolvedService = null; 1083 break; 1084 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED: 1085 /* NNN resolveId errorCode */ 1086 stopGetAddrInfo(id); 1087 removeRequestMap(clientId, id, clientInfo); 1088 clientInfo.onResolveServiceFailed( 1089 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 1090 clientInfo.mResolvedService = null; 1091 break; 1092 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: { 1093 /* NNN resolveId hostname ttl addr interfaceIdx netId */ 1094 final GetAddressInfo info = (GetAddressInfo) obj; 1095 final String address = info.address; 1096 final int netId = info.netId; 1097 InetAddress serviceHost = null; 1098 try { 1099 serviceHost = InetAddress.getByName(address); 1100 } catch (UnknownHostException e) { 1101 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e); 1102 } 1103 1104 // If the resolved service is on an interface without a network, consider it 1105 // as a failure: it would not be usable by apps as they would need 1106 // privileged permissions. 1107 if (netId != NETID_UNSET && serviceHost != null) { 1108 clientInfo.mResolvedService.setHost(serviceHost); 1109 setServiceNetworkForCallback(clientInfo.mResolvedService, 1110 netId, info.interfaceIdx); 1111 clientInfo.onResolveServiceSucceeded( 1112 clientId, clientInfo.mResolvedService); 1113 } else { 1114 clientInfo.onResolveServiceFailed( 1115 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 1116 } 1117 stopGetAddrInfo(id); 1118 removeRequestMap(clientId, id, clientInfo); 1119 clientInfo.mResolvedService = null; 1120 break; 1121 } 1122 default: 1123 return false; 1124 } 1125 return true; 1126 } 1127 1128 @Nullable buildNsdServiceInfoFromMdnsEvent( final MdnsEvent event, int code)1129 private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent( 1130 final MdnsEvent event, int code) { 1131 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; 1132 final String[] typeArray = serviceInfo.getServiceType(); 1133 final String joinedType; 1134 if (typeArray.length == 0 1135 || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) { 1136 Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: " 1137 + Arrays.toString(typeArray)); 1138 return null; 1139 } else { 1140 joinedType = TextUtils.join(".", 1141 Arrays.copyOfRange(typeArray, 0, typeArray.length - 1)); 1142 } 1143 final String serviceType; 1144 switch (code) { 1145 case NsdManager.SERVICE_FOUND: 1146 case NsdManager.SERVICE_LOST: 1147 // For consistency with historical behavior, discovered service types have 1148 // a dot at the end. 1149 serviceType = joinedType + "."; 1150 break; 1151 case RESOLVE_SERVICE_SUCCEEDED: 1152 // For consistency with historical behavior, resolved service types have 1153 // a dot at the beginning. 1154 serviceType = "." + joinedType; 1155 break; 1156 default: 1157 serviceType = joinedType; 1158 break; 1159 } 1160 final String serviceName = serviceInfo.getServiceInstanceName(); 1161 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType); 1162 final Network network = serviceInfo.getNetwork(); 1163 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a 1164 // network for Tethering interface. In other words, the network == null means the 1165 // network has netId = INetd.LOCAL_NET_ID. 1166 setServiceNetworkForCallback( 1167 servInfo, 1168 network == null ? INetd.LOCAL_NET_ID : network.netId, 1169 serviceInfo.getInterfaceIndex()); 1170 return servInfo; 1171 } 1172 handleMdnsDiscoveryManagerEvent( int transactionId, int code, Object obj)1173 private boolean handleMdnsDiscoveryManagerEvent( 1174 int transactionId, int code, Object obj) { 1175 final ClientInfo clientInfo = mIdToClientInfoMap.get(transactionId); 1176 if (clientInfo == null) { 1177 Log.e(TAG, String.format( 1178 "id %d for %d has no client mapping", transactionId, code)); 1179 return false; 1180 } 1181 1182 final MdnsEvent event = (MdnsEvent) obj; 1183 final int clientId = event.mClientId; 1184 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code); 1185 // Errors are already logged if null 1186 if (info == null) return false; 1187 if (DBG) { 1188 Log.d(TAG, String.format("MdnsDiscoveryManager event code=%s transactionId=%d", 1189 NsdManager.nameOf(code), transactionId)); 1190 } 1191 switch (code) { 1192 case NsdManager.SERVICE_FOUND: 1193 clientInfo.onServiceFound(clientId, info); 1194 break; 1195 case NsdManager.SERVICE_LOST: 1196 clientInfo.onServiceLost(clientId, info); 1197 break; 1198 case NsdManager.RESOLVE_SERVICE_SUCCEEDED: { 1199 final ClientRequest request = clientInfo.mClientRequests.get(clientId); 1200 if (request == null) { 1201 Log.e(TAG, "Unknown client request in RESOLVE_SERVICE_SUCCEEDED"); 1202 break; 1203 } 1204 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; 1205 info.setPort(serviceInfo.getPort()); 1206 1207 Map<String, String> attrs = serviceInfo.getAttributes(); 1208 for (Map.Entry<String, String> kv : attrs.entrySet()) { 1209 final String key = kv.getKey(); 1210 try { 1211 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); 1212 } catch (IllegalArgumentException e) { 1213 Log.e(TAG, "Invalid attribute", e); 1214 } 1215 } 1216 final List<InetAddress> addresses = getInetAddresses(serviceInfo); 1217 if (addresses.size() != 0) { 1218 info.setHostAddresses(addresses); 1219 clientInfo.onResolveServiceSucceeded(clientId, info); 1220 } else { 1221 // No address. Notify resolution failure. 1222 clientInfo.onResolveServiceFailed( 1223 clientId, NsdManager.FAILURE_INTERNAL_ERROR); 1224 } 1225 1226 // Unregister the listener immediately like IMDnsEventListener design 1227 if (!(request instanceof DiscoveryManagerRequest)) { 1228 Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event"); 1229 break; 1230 } 1231 stopDiscoveryManagerRequest(request, clientId, transactionId, clientInfo); 1232 break; 1233 } 1234 case NsdManager.SERVICE_UPDATED: { 1235 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; 1236 info.setPort(serviceInfo.getPort()); 1237 1238 Map<String, String> attrs = serviceInfo.getAttributes(); 1239 for (Map.Entry<String, String> kv : attrs.entrySet()) { 1240 final String key = kv.getKey(); 1241 try { 1242 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); 1243 } catch (IllegalArgumentException e) { 1244 Log.e(TAG, "Invalid attribute", e); 1245 } 1246 } 1247 1248 final List<InetAddress> addresses = getInetAddresses(serviceInfo); 1249 info.setHostAddresses(addresses); 1250 clientInfo.onServiceUpdated(clientId, info); 1251 break; 1252 } 1253 case NsdManager.SERVICE_UPDATED_LOST: 1254 clientInfo.onServiceUpdatedLost(clientId); 1255 break; 1256 default: 1257 return false; 1258 } 1259 return true; 1260 } 1261 } 1262 } 1263 1264 @NonNull getInetAddresses(@onNull MdnsServiceInfo serviceInfo)1265 private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) { 1266 final List<String> v4Addrs = serviceInfo.getIpv4Addresses(); 1267 final List<String> v6Addrs = serviceInfo.getIpv6Addresses(); 1268 final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size()); 1269 for (String ipv4Address : v4Addrs) { 1270 try { 1271 addresses.add(InetAddresses.parseNumericAddress(ipv4Address)); 1272 } catch (IllegalArgumentException e) { 1273 Log.wtf(TAG, "Invalid ipv4 address", e); 1274 } 1275 } 1276 for (String ipv6Address : v6Addrs) { 1277 try { 1278 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress( 1279 ipv6Address); 1280 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex())); 1281 } catch (IllegalArgumentException e) { 1282 Log.wtf(TAG, "Invalid ipv6 address", e); 1283 } 1284 } 1285 return addresses; 1286 } 1287 setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx)1288 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) { 1289 switch (netId) { 1290 case NETID_UNSET: 1291 info.setNetwork(null); 1292 break; 1293 case INetd.LOCAL_NET_ID: 1294 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally 1295 // visible / usable for apps, so do not return it. Store the interface 1296 // index instead, so at least if the client tries to resolve the service 1297 // with that NsdServiceInfo, it will be done on the same interface. 1298 // If they recreate the NsdServiceInfo themselves, resolution would be 1299 // done on all interfaces as before T, which should also work. 1300 info.setNetwork(null); 1301 info.setInterfaceIndex(ifaceIdx); 1302 break; 1303 default: 1304 info.setNetwork(new Network(netId)); 1305 } 1306 } 1307 1308 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable 1309 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name 1310 // unescape for getting right service address. See "Notes on DNS Name Escaping" on 1311 // external/mdnsresponder/mDNSShared/dns_sd.h for more details. unescape(String s)1312 private String unescape(String s) { 1313 StringBuilder sb = new StringBuilder(s.length()); 1314 for (int i = 0; i < s.length(); ++i) { 1315 char c = s.charAt(i); 1316 if (c == '\\') { 1317 if (++i >= s.length()) { 1318 Log.e(TAG, "Unexpected end of escape sequence in: " + s); 1319 break; 1320 } 1321 c = s.charAt(i); 1322 if (c != '.' && c != '\\') { 1323 if (i + 2 >= s.length()) { 1324 Log.e(TAG, "Unexpected end of escape sequence in: " + s); 1325 break; 1326 } 1327 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10 1328 + (s.charAt(i + 2) - '0')); 1329 i += 2; 1330 } 1331 } 1332 sb.append(c); 1333 } 1334 return sb.toString(); 1335 } 1336 1337 /** 1338 * Check the given service type is valid and construct it to a service type 1339 * which can use for discovery / resolution service. 1340 * 1341 * <p>The valid service type should be 2 labels, or 3 labels if the query is for a 1342 * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an 1343 * underscore; they are alphanumerical characters or dashes or underscore, except the 1344 * last one that is just alphanumerical. The last label must be _tcp or _udp. 1345 * 1346 * <p>The subtype may also be specified with a comma after the service type, for example 1347 * _type._tcp,_subtype. 1348 * 1349 * @param serviceType the request service type for discovery / resolution service 1350 * @return constructed service type or null if the given service type is invalid. 1351 */ 1352 @Nullable parseTypeAndSubtype(String serviceType)1353 public static Pair<String, String> parseTypeAndSubtype(String serviceType) { 1354 if (TextUtils.isEmpty(serviceType)) return null; 1355 1356 final String typeOrSubtypePattern = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]"; 1357 final Pattern serviceTypePattern = Pattern.compile( 1358 // Optional leading subtype (_subtype._type._tcp) 1359 // (?: xxx) is a non-capturing parenthesis, don't capture the dot 1360 "^(?:(" + typeOrSubtypePattern + ")\\.)?" 1361 // Actual type (_type._tcp.local) 1362 + "(" + typeOrSubtypePattern + "\\._(?:tcp|udp))" 1363 // Drop '.' at the end of service type that is compatible with old backend. 1364 // e.g. allow "_type._tcp.local." 1365 + "\\.?" 1366 // Optional subtype after comma, for "_type._tcp,_subtype" format 1367 + "(?:,(" + typeOrSubtypePattern + "))?" 1368 + "$"); 1369 final Matcher matcher = serviceTypePattern.matcher(serviceType); 1370 if (!matcher.matches()) return null; 1371 // Use the subtype either at the beginning or after the comma 1372 final String subtype = matcher.group(1) != null ? matcher.group(1) : matcher.group(3); 1373 return new Pair<>(matcher.group(2), subtype); 1374 } 1375 1376 @VisibleForTesting NsdService(Context ctx, Handler handler, long cleanupDelayMs)1377 NsdService(Context ctx, Handler handler, long cleanupDelayMs) { 1378 this(ctx, handler, cleanupDelayMs, new Dependencies()); 1379 } 1380 1381 @VisibleForTesting NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps)1382 NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) { 1383 mCleanupDelayMs = cleanupDelayMs; 1384 mContext = ctx; 1385 mNsdStateMachine = new NsdStateMachine(TAG, handler); 1386 mNsdStateMachine.start(); 1387 mMDnsManager = ctx.getSystemService(MDnsManager.class); 1388 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine); 1389 mDeps = deps; 1390 1391 mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(), 1392 LOGGER.forSubComponent("MdnsSocketProvider")); 1393 // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all 1394 // address events are received. 1395 handler.post(mMdnsSocketProvider::startNetLinkMonitor); 1396 mMdnsSocketClient = 1397 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider); 1398 mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(), 1399 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager")); 1400 handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager)); 1401 mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider, 1402 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser")); 1403 } 1404 1405 /** 1406 * Dependencies of NsdService, for injection in tests. 1407 */ 1408 @VisibleForTesting 1409 public static class Dependencies { 1410 /** 1411 * Check whether the MdnsDiscoveryManager feature is enabled. 1412 * 1413 * @param context The global context information about an app environment. 1414 * @return true if the MdnsDiscoveryManager feature is enabled. 1415 */ isMdnsDiscoveryManagerEnabled(Context context)1416 public boolean isMdnsDiscoveryManagerEnabled(Context context) { 1417 return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING, 1418 MDNS_DISCOVERY_MANAGER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME, 1419 false /* defaultEnabled */); 1420 } 1421 1422 /** 1423 * Check whether the MdnsAdvertiser feature is enabled. 1424 * 1425 * @param context The global context information about an app environment. 1426 * @return true if the MdnsAdvertiser feature is enabled. 1427 */ isMdnsAdvertiserEnabled(Context context)1428 public boolean isMdnsAdvertiserEnabled(Context context) { 1429 return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING, 1430 MDNS_ADVERTISER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME, 1431 false /* defaultEnabled */); 1432 } 1433 1434 /** 1435 * Get the type allowlist flag value. 1436 * @see #MDNS_TYPE_ALLOWLIST_FLAGS 1437 */ 1438 @Nullable getTypeAllowlistFlags()1439 public String getTypeAllowlistFlags() { 1440 return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING, 1441 MDNS_TYPE_ALLOWLIST_FLAGS, null); 1442 } 1443 1444 /** 1445 * @see DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean) 1446 */ isFeatureEnabled(Context context, String feature)1447 public boolean isFeatureEnabled(Context context, String feature) { 1448 return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING, 1449 feature, DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */); 1450 } 1451 1452 /** 1453 * @see MdnsDiscoveryManager 1454 */ makeMdnsDiscoveryManager( @onNull ExecutorProvider executorProvider, @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog)1455 public MdnsDiscoveryManager makeMdnsDiscoveryManager( 1456 @NonNull ExecutorProvider executorProvider, 1457 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog) { 1458 return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog); 1459 } 1460 1461 /** 1462 * @see MdnsAdvertiser 1463 */ makeMdnsAdvertiser( @onNull Looper looper, @NonNull MdnsSocketProvider socketProvider, @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog)1464 public MdnsAdvertiser makeMdnsAdvertiser( 1465 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider, 1466 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog) { 1467 return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog); 1468 } 1469 1470 /** 1471 * @see MdnsSocketProvider 1472 */ makeMdnsSocketProvider(@onNull Context context, @NonNull Looper looper, @NonNull SharedLog sharedLog)1473 public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context, 1474 @NonNull Looper looper, @NonNull SharedLog sharedLog) { 1475 return new MdnsSocketProvider(context, looper, sharedLog); 1476 } 1477 } 1478 1479 /** 1480 * Return whether a type is allowlisted to use the Java backend. 1481 * @param type The service type 1482 * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or 1483 * {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}. 1484 */ isTypeAllowlistedForJavaBackend(@ullable String type, @NonNull String flagPrefix)1485 private boolean isTypeAllowlistedForJavaBackend(@Nullable String type, 1486 @NonNull String flagPrefix) { 1487 if (type == null) return false; 1488 final String typesConfig = mDeps.getTypeAllowlistFlags(); 1489 if (TextUtils.isEmpty(typesConfig)) return false; 1490 1491 final String mappingPrefix = type + ":"; 1492 String mappedFlag = null; 1493 for (String mapping : TextUtils.split(typesConfig, ",")) { 1494 if (mapping.startsWith(mappingPrefix)) { 1495 mappedFlag = mapping.substring(mappingPrefix.length()); 1496 break; 1497 } 1498 } 1499 1500 if (mappedFlag == null) return false; 1501 1502 return mDeps.isFeatureEnabled(mContext, 1503 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX); 1504 } 1505 useDiscoveryManagerForType(@ullable String type)1506 private boolean useDiscoveryManagerForType(@Nullable String type) { 1507 return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX); 1508 } 1509 useAdvertiserForType(@ullable String type)1510 private boolean useAdvertiserForType(@Nullable String type) { 1511 return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX); 1512 } 1513 create(Context context)1514 public static NsdService create(Context context) { 1515 HandlerThread thread = new HandlerThread(TAG); 1516 thread.start(); 1517 Handler handler = new Handler(thread.getLooper()); 1518 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS); 1519 return service; 1520 } 1521 1522 private static class MDnsEventCallback extends IMDnsEventListener.Stub { 1523 private final StateMachine mStateMachine; 1524 MDnsEventCallback(StateMachine sm)1525 MDnsEventCallback(StateMachine sm) { 1526 mStateMachine = sm; 1527 } 1528 1529 @Override onServiceRegistrationStatus(final RegistrationInfo status)1530 public void onServiceRegistrationStatus(final RegistrationInfo status) { 1531 mStateMachine.sendMessage( 1532 MDNS_SERVICE_EVENT, status.result, status.id, status); 1533 } 1534 1535 @Override onServiceDiscoveryStatus(final DiscoveryInfo status)1536 public void onServiceDiscoveryStatus(final DiscoveryInfo status) { 1537 mStateMachine.sendMessage( 1538 MDNS_SERVICE_EVENT, status.result, status.id, status); 1539 } 1540 1541 @Override onServiceResolutionStatus(final ResolutionInfo status)1542 public void onServiceResolutionStatus(final ResolutionInfo status) { 1543 mStateMachine.sendMessage( 1544 MDNS_SERVICE_EVENT, status.result, status.id, status); 1545 } 1546 1547 @Override onGettingServiceAddressStatus(final GetAddressInfo status)1548 public void onGettingServiceAddressStatus(final GetAddressInfo status) { 1549 mStateMachine.sendMessage( 1550 MDNS_SERVICE_EVENT, status.result, status.id, status); 1551 } 1552 1553 @Override getInterfaceVersion()1554 public int getInterfaceVersion() throws RemoteException { 1555 return this.VERSION; 1556 } 1557 1558 @Override getInterfaceHash()1559 public String getInterfaceHash() throws RemoteException { 1560 return this.HASH; 1561 } 1562 } 1563 1564 private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback { 1565 @Override onRegisterServiceSucceeded(int serviceId, NsdServiceInfo registeredInfo)1566 public void onRegisterServiceSucceeded(int serviceId, NsdServiceInfo registeredInfo) { 1567 final ClientInfo clientInfo = getClientInfoOrLog(serviceId); 1568 if (clientInfo == null) return; 1569 1570 final int clientId = getClientIdOrLog(clientInfo, serviceId); 1571 if (clientId < 0) return; 1572 1573 // onRegisterServiceSucceeded only has the service name in its info. This aligns with 1574 // historical behavior. 1575 final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null); 1576 clientInfo.onRegisterServiceSucceeded(clientId, cbInfo); 1577 } 1578 1579 @Override onRegisterServiceFailed(int serviceId, int errorCode)1580 public void onRegisterServiceFailed(int serviceId, int errorCode) { 1581 final ClientInfo clientInfo = getClientInfoOrLog(serviceId); 1582 if (clientInfo == null) return; 1583 1584 final int clientId = getClientIdOrLog(clientInfo, serviceId); 1585 if (clientId < 0) return; 1586 1587 clientInfo.onRegisterServiceFailed(clientId, errorCode); 1588 } 1589 getClientInfoOrLog(int serviceId)1590 private ClientInfo getClientInfoOrLog(int serviceId) { 1591 final ClientInfo clientInfo = mIdToClientInfoMap.get(serviceId); 1592 if (clientInfo == null) { 1593 Log.e(TAG, String.format("Callback for service %d has no client", serviceId)); 1594 } 1595 return clientInfo; 1596 } 1597 getClientIdOrLog(@onNull ClientInfo info, int serviceId)1598 private int getClientIdOrLog(@NonNull ClientInfo info, int serviceId) { 1599 final int clientId = info.getClientId(serviceId); 1600 if (clientId < 0) { 1601 Log.e(TAG, String.format("Client ID not found for service %d", serviceId)); 1602 } 1603 return clientId; 1604 } 1605 } 1606 1607 private static class ConnectorArgs { 1608 @NonNull public final NsdServiceConnector connector; 1609 @NonNull public final INsdManagerCallback callback; 1610 public final boolean useJavaBackend; 1611 public final int uid; 1612 ConnectorArgs(@onNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback, boolean useJavaBackend, int uid)1613 ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback, 1614 boolean useJavaBackend, int uid) { 1615 this.connector = connector; 1616 this.callback = callback; 1617 this.useJavaBackend = useJavaBackend; 1618 this.uid = uid; 1619 } 1620 } 1621 1622 @Override connect(INsdManagerCallback cb, boolean useJavaBackend)1623 public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) { 1624 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); 1625 if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend); 1626 final INsdServiceConnector connector = new NsdServiceConnector(); 1627 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT, 1628 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, 1629 Binder.getCallingUid()))); 1630 return connector; 1631 } 1632 1633 private static class ListenerArgs { 1634 public final NsdServiceConnector connector; 1635 public final NsdServiceInfo serviceInfo; ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo)1636 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) { 1637 this.connector = connector; 1638 this.serviceInfo = serviceInfo; 1639 } 1640 } 1641 1642 private class NsdServiceConnector extends INsdServiceConnector.Stub 1643 implements IBinder.DeathRecipient { 1644 @Override registerService(int listenerKey, NsdServiceInfo serviceInfo)1645 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) { 1646 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1647 NsdManager.REGISTER_SERVICE, 0, listenerKey, 1648 new ListenerArgs(this, serviceInfo))); 1649 } 1650 1651 @Override unregisterService(int listenerKey)1652 public void unregisterService(int listenerKey) { 1653 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1654 NsdManager.UNREGISTER_SERVICE, 0, listenerKey, 1655 new ListenerArgs(this, null))); 1656 } 1657 1658 @Override discoverServices(int listenerKey, NsdServiceInfo serviceInfo)1659 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) { 1660 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1661 NsdManager.DISCOVER_SERVICES, 0, listenerKey, 1662 new ListenerArgs(this, serviceInfo))); 1663 } 1664 1665 @Override stopDiscovery(int listenerKey)1666 public void stopDiscovery(int listenerKey) { 1667 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1668 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null))); 1669 } 1670 1671 @Override resolveService(int listenerKey, NsdServiceInfo serviceInfo)1672 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) { 1673 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1674 NsdManager.RESOLVE_SERVICE, 0, listenerKey, 1675 new ListenerArgs(this, serviceInfo))); 1676 } 1677 1678 @Override stopResolution(int listenerKey)1679 public void stopResolution(int listenerKey) { 1680 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1681 NsdManager.STOP_RESOLUTION, 0, listenerKey, new ListenerArgs(this, null))); 1682 } 1683 1684 @Override registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo)1685 public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) { 1686 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1687 NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey, 1688 new ListenerArgs(this, serviceInfo))); 1689 } 1690 1691 @Override unregisterServiceInfoCallback(int listenerKey)1692 public void unregisterServiceInfoCallback(int listenerKey) { 1693 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1694 NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey, 1695 new ListenerArgs(this, null))); 1696 } 1697 1698 @Override startDaemon()1699 public void startDaemon() { 1700 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 1701 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null))); 1702 } 1703 1704 @Override binderDied()1705 public void binderDied() { 1706 mNsdStateMachine.sendMessage( 1707 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this)); 1708 } 1709 } 1710 sendNsdStateChangeBroadcast(boolean isEnabled)1711 private void sendNsdStateChangeBroadcast(boolean isEnabled) { 1712 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED); 1713 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1714 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED; 1715 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState); 1716 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1717 } 1718 getUniqueId()1719 private int getUniqueId() { 1720 if (++mUniqueId == INVALID_ID) return ++mUniqueId; 1721 return mUniqueId; 1722 } 1723 registerService(int regId, NsdServiceInfo service)1724 private boolean registerService(int regId, NsdServiceInfo service) { 1725 if (DBG) { 1726 Log.d(TAG, "registerService: " + regId + " " + service); 1727 } 1728 String name = service.getServiceName(); 1729 String type = service.getServiceType(); 1730 int port = service.getPort(); 1731 byte[] textRecord = service.getTxtRecord(); 1732 final int registerInterface = getNetworkInterfaceIndex(service); 1733 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) { 1734 Log.e(TAG, "Interface to register service on not found"); 1735 return false; 1736 } 1737 return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface); 1738 } 1739 unregisterService(int regId)1740 private boolean unregisterService(int regId) { 1741 return mMDnsManager.stopOperation(regId); 1742 } 1743 discoverServices(int discoveryId, NsdServiceInfo serviceInfo)1744 private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) { 1745 final String type = serviceInfo.getServiceType(); 1746 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo); 1747 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) { 1748 Log.e(TAG, "Interface to discover service on not found"); 1749 return false; 1750 } 1751 return mMDnsManager.discover(discoveryId, type, discoverInterface); 1752 } 1753 stopServiceDiscovery(int discoveryId)1754 private boolean stopServiceDiscovery(int discoveryId) { 1755 return mMDnsManager.stopOperation(discoveryId); 1756 } 1757 resolveService(int resolveId, NsdServiceInfo service)1758 private boolean resolveService(int resolveId, NsdServiceInfo service) { 1759 final String name = service.getServiceName(); 1760 final String type = service.getServiceType(); 1761 final int resolveInterface = getNetworkInterfaceIndex(service); 1762 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) { 1763 Log.e(TAG, "Interface to resolve service on not found"); 1764 return false; 1765 } 1766 return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface); 1767 } 1768 1769 /** 1770 * Guess the interface to use to resolve or discover a service on a specific network. 1771 * 1772 * This is an imperfect guess, as for example the network may be gone or not yet fully 1773 * registered. This is fine as failing is correct if the network is gone, and a client 1774 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also 1775 * this is to support the legacy mdnsresponder implementation, which historically resolved 1776 * services on an unspecified network. 1777 */ getNetworkInterfaceIndex(NsdServiceInfo serviceInfo)1778 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) { 1779 final Network network = serviceInfo.getNetwork(); 1780 if (network == null) { 1781 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was 1782 // provided by NsdService from discovery results, and the service was found on an 1783 // interface that has no app-usable Network). 1784 if (serviceInfo.getInterfaceIndex() != 0) { 1785 return serviceInfo.getInterfaceIndex(); 1786 } 1787 return IFACE_IDX_ANY; 1788 } 1789 1790 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 1791 if (cm == null) { 1792 Log.wtf(TAG, "No ConnectivityManager for resolveService"); 1793 return IFACE_IDX_ANY; 1794 } 1795 final LinkProperties lp = cm.getLinkProperties(network); 1796 if (lp == null) return IFACE_IDX_ANY; 1797 1798 // Only resolve on non-stacked interfaces 1799 final NetworkInterface iface; 1800 try { 1801 iface = NetworkInterface.getByName(lp.getInterfaceName()); 1802 } catch (SocketException e) { 1803 Log.e(TAG, "Error querying interface", e); 1804 return IFACE_IDX_ANY; 1805 } 1806 1807 if (iface == null) { 1808 Log.e(TAG, "Interface not found: " + lp.getInterfaceName()); 1809 return IFACE_IDX_ANY; 1810 } 1811 1812 return iface.getIndex(); 1813 } 1814 stopResolveService(int resolveId)1815 private boolean stopResolveService(int resolveId) { 1816 return mMDnsManager.stopOperation(resolveId); 1817 } 1818 getAddrInfo(int resolveId, String hostname, int interfaceIdx)1819 private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) { 1820 return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx); 1821 } 1822 stopGetAddrInfo(int resolveId)1823 private boolean stopGetAddrInfo(int resolveId) { 1824 return mMDnsManager.stopOperation(resolveId); 1825 } 1826 1827 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)1828 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 1829 if (!PermissionUtils.checkDumpPermission(mContext, TAG, writer)) return; 1830 1831 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 1832 // Dump state machine logs 1833 mNsdStateMachine.dump(fd, pw, args); 1834 1835 // Dump service and clients logs 1836 pw.println(); 1837 pw.println("Logs:"); 1838 pw.increaseIndent(); 1839 mServiceLogs.reverseDump(pw); 1840 pw.decreaseIndent(); 1841 } 1842 1843 private abstract static class ClientRequest { 1844 private final int mGlobalId; 1845 ClientRequest(int globalId)1846 private ClientRequest(int globalId) { 1847 mGlobalId = globalId; 1848 } 1849 } 1850 1851 private static class LegacyClientRequest extends ClientRequest { 1852 private final int mRequestCode; 1853 LegacyClientRequest(int globalId, int requestCode)1854 private LegacyClientRequest(int globalId, int requestCode) { 1855 super(globalId); 1856 mRequestCode = requestCode; 1857 } 1858 } 1859 1860 private static class AdvertiserClientRequest extends ClientRequest { AdvertiserClientRequest(int globalId)1861 private AdvertiserClientRequest(int globalId) { 1862 super(globalId); 1863 } 1864 } 1865 1866 private static class DiscoveryManagerRequest extends ClientRequest { 1867 @NonNull 1868 private final MdnsListener mListener; 1869 DiscoveryManagerRequest(int globalId, @NonNull MdnsListener listener)1870 private DiscoveryManagerRequest(int globalId, @NonNull MdnsListener listener) { 1871 super(globalId); 1872 mListener = listener; 1873 } 1874 } 1875 1876 /* Information tracked per client */ 1877 private class ClientInfo { 1878 1879 private static final int MAX_LIMIT = 10; 1880 private final INsdManagerCallback mCb; 1881 /* Remembers a resolved service until getaddrinfo completes */ 1882 private NsdServiceInfo mResolvedService; 1883 1884 /* A map from client-side ID (listenerKey) to the request */ 1885 private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>(); 1886 1887 // The target SDK of this client < Build.VERSION_CODES.S 1888 private boolean mIsPreSClient = false; 1889 // The flag of using java backend if the client's target SDK >= U 1890 private final boolean mUseJavaBackend; 1891 // Store client logs 1892 private final SharedLog mClientLogs; 1893 ClientInfo(INsdManagerCallback cb, boolean useJavaBackend, SharedLog sharedLog)1894 private ClientInfo(INsdManagerCallback cb, boolean useJavaBackend, SharedLog sharedLog) { 1895 mCb = cb; 1896 mUseJavaBackend = useJavaBackend; 1897 mClientLogs = sharedLog; 1898 mClientLogs.log("New client. useJavaBackend=" + useJavaBackend); 1899 } 1900 1901 @Override toString()1902 public String toString() { 1903 StringBuilder sb = new StringBuilder(); 1904 sb.append("mResolvedService ").append(mResolvedService).append("\n"); 1905 sb.append("mIsLegacy ").append(mIsPreSClient).append("\n"); 1906 for (int i = 0; i < mClientRequests.size(); i++) { 1907 int clientID = mClientRequests.keyAt(i); 1908 sb.append("clientId ") 1909 .append(clientID) 1910 .append(" mDnsId ").append(mClientRequests.valueAt(i).mGlobalId) 1911 .append(" type ").append( 1912 mClientRequests.valueAt(i).getClass().getSimpleName()) 1913 .append("\n"); 1914 } 1915 return sb.toString(); 1916 } 1917 isPreSClient()1918 private boolean isPreSClient() { 1919 return mIsPreSClient; 1920 } 1921 setPreSClient()1922 private void setPreSClient() { 1923 mIsPreSClient = true; 1924 } 1925 unregisterMdnsListenerFromRequest(ClientRequest request)1926 private void unregisterMdnsListenerFromRequest(ClientRequest request) { 1927 final MdnsListener listener = 1928 ((DiscoveryManagerRequest) request).mListener; 1929 mMdnsDiscoveryManager.unregisterListener( 1930 listener.getListenedServiceType(), listener); 1931 } 1932 1933 // Remove any pending requests from the global map when we get rid of a client, 1934 // and send cancellations to the daemon. expungeAllRequests()1935 private void expungeAllRequests() { 1936 mClientLogs.log("Client unregistered. expungeAllRequests!"); 1937 // TODO: to keep handler responsive, do not clean all requests for that client at once. 1938 for (int i = 0; i < mClientRequests.size(); i++) { 1939 final int clientId = mClientRequests.keyAt(i); 1940 final ClientRequest request = mClientRequests.valueAt(i); 1941 final int globalId = request.mGlobalId; 1942 mIdToClientInfoMap.remove(globalId); 1943 if (DBG) { 1944 Log.d(TAG, "Terminating client-ID " + clientId 1945 + " global-ID " + globalId + " type " + mClientRequests.get(clientId)); 1946 } 1947 1948 if (request instanceof DiscoveryManagerRequest) { 1949 unregisterMdnsListenerFromRequest(request); 1950 continue; 1951 } 1952 1953 if (request instanceof AdvertiserClientRequest) { 1954 mAdvertiser.removeService(globalId); 1955 continue; 1956 } 1957 1958 if (!(request instanceof LegacyClientRequest)) { 1959 throw new IllegalStateException("Unknown request type: " + request.getClass()); 1960 } 1961 1962 switch (((LegacyClientRequest) request).mRequestCode) { 1963 case NsdManager.DISCOVER_SERVICES: 1964 stopServiceDiscovery(globalId); 1965 break; 1966 case NsdManager.RESOLVE_SERVICE: 1967 stopResolveService(globalId); 1968 break; 1969 case NsdManager.REGISTER_SERVICE: 1970 unregisterService(globalId); 1971 break; 1972 default: 1973 break; 1974 } 1975 } 1976 mClientRequests.clear(); 1977 } 1978 1979 // mClientRequests is a sparse array of listener id -> ClientRequest. For a given 1980 // mDnsClient id, return the corresponding listener id. mDnsClient id is also called a 1981 // global id. getClientId(final int globalId)1982 private int getClientId(final int globalId) { 1983 for (int i = 0; i < mClientRequests.size(); i++) { 1984 if (mClientRequests.valueAt(i).mGlobalId == globalId) { 1985 return mClientRequests.keyAt(i); 1986 } 1987 } 1988 return -1; 1989 } 1990 log(String message)1991 private void log(String message) { 1992 mClientLogs.log(message); 1993 } 1994 onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info)1995 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { 1996 try { 1997 mCb.onDiscoverServicesStarted(listenerKey, info); 1998 } catch (RemoteException e) { 1999 Log.e(TAG, "Error calling onDiscoverServicesStarted", e); 2000 } 2001 } 2002 onDiscoverServicesFailed(int listenerKey, int error)2003 void onDiscoverServicesFailed(int listenerKey, int error) { 2004 try { 2005 mCb.onDiscoverServicesFailed(listenerKey, error); 2006 } catch (RemoteException e) { 2007 Log.e(TAG, "Error calling onDiscoverServicesFailed", e); 2008 } 2009 } 2010 onServiceFound(int listenerKey, NsdServiceInfo info)2011 void onServiceFound(int listenerKey, NsdServiceInfo info) { 2012 try { 2013 mCb.onServiceFound(listenerKey, info); 2014 } catch (RemoteException e) { 2015 Log.e(TAG, "Error calling onServiceFound(", e); 2016 } 2017 } 2018 onServiceLost(int listenerKey, NsdServiceInfo info)2019 void onServiceLost(int listenerKey, NsdServiceInfo info) { 2020 try { 2021 mCb.onServiceLost(listenerKey, info); 2022 } catch (RemoteException e) { 2023 Log.e(TAG, "Error calling onServiceLost(", e); 2024 } 2025 } 2026 onStopDiscoveryFailed(int listenerKey, int error)2027 void onStopDiscoveryFailed(int listenerKey, int error) { 2028 try { 2029 mCb.onStopDiscoveryFailed(listenerKey, error); 2030 } catch (RemoteException e) { 2031 Log.e(TAG, "Error calling onStopDiscoveryFailed", e); 2032 } 2033 } 2034 onStopDiscoverySucceeded(int listenerKey)2035 void onStopDiscoverySucceeded(int listenerKey) { 2036 try { 2037 mCb.onStopDiscoverySucceeded(listenerKey); 2038 } catch (RemoteException e) { 2039 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e); 2040 } 2041 } 2042 onRegisterServiceFailed(int listenerKey, int error)2043 void onRegisterServiceFailed(int listenerKey, int error) { 2044 try { 2045 mCb.onRegisterServiceFailed(listenerKey, error); 2046 } catch (RemoteException e) { 2047 Log.e(TAG, "Error calling onRegisterServiceFailed", e); 2048 } 2049 } 2050 onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info)2051 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { 2052 try { 2053 mCb.onRegisterServiceSucceeded(listenerKey, info); 2054 } catch (RemoteException e) { 2055 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e); 2056 } 2057 } 2058 onUnregisterServiceFailed(int listenerKey, int error)2059 void onUnregisterServiceFailed(int listenerKey, int error) { 2060 try { 2061 mCb.onUnregisterServiceFailed(listenerKey, error); 2062 } catch (RemoteException e) { 2063 Log.e(TAG, "Error calling onUnregisterServiceFailed", e); 2064 } 2065 } 2066 onUnregisterServiceSucceeded(int listenerKey)2067 void onUnregisterServiceSucceeded(int listenerKey) { 2068 try { 2069 mCb.onUnregisterServiceSucceeded(listenerKey); 2070 } catch (RemoteException e) { 2071 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e); 2072 } 2073 } 2074 onResolveServiceFailed(int listenerKey, int error)2075 void onResolveServiceFailed(int listenerKey, int error) { 2076 try { 2077 mCb.onResolveServiceFailed(listenerKey, error); 2078 } catch (RemoteException e) { 2079 Log.e(TAG, "Error calling onResolveServiceFailed", e); 2080 } 2081 } 2082 onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info)2083 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { 2084 try { 2085 mCb.onResolveServiceSucceeded(listenerKey, info); 2086 } catch (RemoteException e) { 2087 Log.e(TAG, "Error calling onResolveServiceSucceeded", e); 2088 } 2089 } 2090 onStopResolutionFailed(int listenerKey, int error)2091 void onStopResolutionFailed(int listenerKey, int error) { 2092 try { 2093 mCb.onStopResolutionFailed(listenerKey, error); 2094 } catch (RemoteException e) { 2095 Log.e(TAG, "Error calling onStopResolutionFailed", e); 2096 } 2097 } 2098 onStopResolutionSucceeded(int listenerKey)2099 void onStopResolutionSucceeded(int listenerKey) { 2100 try { 2101 mCb.onStopResolutionSucceeded(listenerKey); 2102 } catch (RemoteException e) { 2103 Log.e(TAG, "Error calling onStopResolutionSucceeded", e); 2104 } 2105 } 2106 onServiceInfoCallbackRegistrationFailed(int listenerKey, int error)2107 void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) { 2108 try { 2109 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error); 2110 } catch (RemoteException e) { 2111 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e); 2112 } 2113 } 2114 onServiceUpdated(int listenerKey, NsdServiceInfo info)2115 void onServiceUpdated(int listenerKey, NsdServiceInfo info) { 2116 try { 2117 mCb.onServiceUpdated(listenerKey, info); 2118 } catch (RemoteException e) { 2119 Log.e(TAG, "Error calling onServiceUpdated", e); 2120 } 2121 } 2122 onServiceUpdatedLost(int listenerKey)2123 void onServiceUpdatedLost(int listenerKey) { 2124 try { 2125 mCb.onServiceUpdatedLost(listenerKey); 2126 } catch (RemoteException e) { 2127 Log.e(TAG, "Error calling onServiceUpdatedLost", e); 2128 } 2129 } 2130 onServiceInfoCallbackUnregistered(int listenerKey)2131 void onServiceInfoCallbackUnregistered(int listenerKey) { 2132 try { 2133 mCb.onServiceInfoCallbackUnregistered(listenerKey); 2134 } catch (RemoteException e) { 2135 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e); 2136 } 2137 } 2138 } 2139 } 2140