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.Manifest.permission.DEVICE_POWER; 20 import static android.Manifest.permission.NETWORK_SETTINGS; 21 import static android.Manifest.permission.NETWORK_STACK; 22 import static android.net.ConnectivityManager.NETID_UNSET; 23 import static android.net.NetworkCapabilities.TRANSPORT_VPN; 24 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 25 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; 26 import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT; 27 import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT; 28 import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED; 29 import static android.net.nsd.NsdManager.SUBTYPE_LABEL_REGEX; 30 import static android.net.nsd.NsdManager.TYPE_REGEX; 31 import static android.os.Process.SYSTEM_UID; 32 import static android.provider.DeviceConfig.NAMESPACE_TETHERING; 33 34 import static com.android.modules.utils.build.SdkLevel.isAtLeastU; 35 import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE; 36 import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics; 37 import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET; 38 import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH; 39 import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE; 40 import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE; 41 import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock; 42 43 import android.annotation.NonNull; 44 import android.annotation.Nullable; 45 import android.annotation.RequiresApi; 46 import android.app.ActivityManager; 47 import android.content.Context; 48 import android.content.Intent; 49 import android.net.ConnectivityManager; 50 import android.net.INetd; 51 import android.net.InetAddresses; 52 import android.net.LinkProperties; 53 import android.net.Network; 54 import android.net.mdns.aidl.DiscoveryInfo; 55 import android.net.mdns.aidl.GetAddressInfo; 56 import android.net.mdns.aidl.IMDnsEventListener; 57 import android.net.mdns.aidl.RegistrationInfo; 58 import android.net.mdns.aidl.ResolutionInfo; 59 import android.net.nsd.AdvertisingRequest; 60 import android.net.nsd.DiscoveryRequest; 61 import android.net.nsd.INsdManager; 62 import android.net.nsd.INsdManagerCallback; 63 import android.net.nsd.INsdServiceConnector; 64 import android.net.nsd.IOffloadEngine; 65 import android.net.nsd.MDnsManager; 66 import android.net.nsd.NsdManager; 67 import android.net.nsd.NsdServiceInfo; 68 import android.net.nsd.OffloadEngine; 69 import android.net.nsd.OffloadServiceInfo; 70 import android.net.wifi.WifiManager; 71 import android.os.Binder; 72 import android.os.Build; 73 import android.os.Handler; 74 import android.os.HandlerThread; 75 import android.os.IBinder; 76 import android.os.Looper; 77 import android.os.Message; 78 import android.os.RemoteCallbackList; 79 import android.os.RemoteException; 80 import android.os.UserHandle; 81 import android.provider.DeviceConfig; 82 import android.text.TextUtils; 83 import android.util.ArraySet; 84 import android.util.Log; 85 import android.util.Pair; 86 import android.util.SparseArray; 87 88 import com.android.internal.annotations.VisibleForTesting; 89 import com.android.internal.util.IndentingPrintWriter; 90 import com.android.internal.util.State; 91 import com.android.internal.util.StateMachine; 92 import com.android.metrics.NetworkNsdReportedMetrics; 93 import com.android.modules.utils.build.SdkLevel; 94 import com.android.net.module.util.CollectionUtils; 95 import com.android.net.module.util.DeviceConfigUtils; 96 import com.android.net.module.util.HandlerUtils; 97 import com.android.net.module.util.InetAddressUtils; 98 import com.android.net.module.util.PermissionUtils; 99 import com.android.net.module.util.SharedLog; 100 import com.android.server.connectivity.mdns.ExecutorProvider; 101 import com.android.server.connectivity.mdns.MdnsAdvertiser; 102 import com.android.server.connectivity.mdns.MdnsAdvertisingOptions; 103 import com.android.server.connectivity.mdns.MdnsDiscoveryManager; 104 import com.android.server.connectivity.mdns.MdnsFeatureFlags; 105 import com.android.server.connectivity.mdns.MdnsInterfaceSocket; 106 import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient; 107 import com.android.server.connectivity.mdns.MdnsSearchOptions; 108 import com.android.server.connectivity.mdns.MdnsServiceBrowserListener; 109 import com.android.server.connectivity.mdns.MdnsServiceInfo; 110 import com.android.server.connectivity.mdns.MdnsSocketProvider; 111 import com.android.server.connectivity.mdns.util.MdnsUtils; 112 113 import java.io.FileDescriptor; 114 import java.io.PrintWriter; 115 import java.net.Inet6Address; 116 import java.net.InetAddress; 117 import java.net.NetworkInterface; 118 import java.net.SocketException; 119 import java.net.UnknownHostException; 120 import java.time.Duration; 121 import java.util.ArrayList; 122 import java.util.Arrays; 123 import java.util.Collection; 124 import java.util.Collections; 125 import java.util.HashMap; 126 import java.util.LinkedHashMap; 127 import java.util.List; 128 import java.util.Map; 129 import java.util.Objects; 130 import java.util.Set; 131 import java.util.regex.Matcher; 132 import java.util.regex.Pattern; 133 134 /** 135 * Network Service Discovery Service handles remote service discovery operation requests by 136 * implementing the INsdManager interface. 137 * 138 * @hide 139 */ 140 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 141 public class NsdService extends INsdManager.Stub { 142 private static final String TAG = "NsdService"; 143 private static final String MDNS_TAG = "mDnsConnector"; 144 /** 145 * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder 146 * implementation. 147 */ 148 private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version"; 149 private static final String LOCAL_DOMAIN_NAME = "local"; 150 151 /** 152 * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder 153 * implementation. 154 */ 155 private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version"; 156 157 /** 158 * Comma-separated list of type:flag mappings indicating the flags to use to allowlist 159 * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type. 160 * 161 * For example _mytype._tcp.local and _othertype._tcp.local would be configured with: 162 * _mytype._tcp:mytype,_othertype._tcp.local:othertype 163 * 164 * In which case the flags: 165 * "mdns_discovery_manager_allowlist_mytype_version", 166 * "mdns_advertiser_allowlist_mytype_version", 167 * "mdns_discovery_manager_allowlist_othertype_version", 168 * "mdns_advertiser_allowlist_othertype_version" 169 * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will 170 * be read with 171 * {@link DeviceConfigUtils#isTetheringFeatureEnabled} 172 * 173 * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX 174 * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX 175 * @see #MDNS_ALLOWLIST_FLAG_SUFFIX 176 */ 177 private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags"; 178 179 private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX = 180 "mdns_discovery_manager_allowlist_"; 181 private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX = 182 "mdns_advertiser_allowlist_"; 183 private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version"; 184 185 private static final String FORCE_ENABLE_FLAG_FOR_TEST_PREFIX = "test_"; 186 187 @VisibleForTesting 188 static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF = 189 "mdns_config_running_app_active_importance_cutoff"; 190 @VisibleForTesting 191 static final int DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF = 192 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 193 private final int mRunningAppActiveImportanceCutoff; 194 195 public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 196 private static final long CLEANUP_DELAY_MS = 10000; 197 private static final int IFACE_IDX_ANY = 0; 198 private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100; 199 @VisibleForTesting 200 static final int NO_TRANSACTION = -1; 201 private static final int NO_SENT_QUERY_COUNT = 0; 202 private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000; 203 private static final int MAX_SUBTYPE_COUNT = 100; 204 private static final int DNSSEC_PROTOCOL = 3; 205 private static final SharedLog LOGGER = new SharedLog("serviceDiscovery"); 206 207 private final Context mContext; 208 private final NsdStateMachine mNsdStateMachine; 209 // It can be null on V+ device since mdns native service provided by netd is removed. 210 private final @Nullable MDnsManager mMDnsManager; 211 private final MDnsEventCallback mMDnsEventCallback; 212 @NonNull 213 private final Dependencies mDeps; 214 @NonNull 215 private final MdnsMultinetworkSocketClient mMdnsSocketClient; 216 @NonNull 217 private final MdnsDiscoveryManager mMdnsDiscoveryManager; 218 @NonNull 219 private final MdnsSocketProvider mMdnsSocketProvider; 220 @NonNull 221 private final MdnsAdvertiser mAdvertiser; 222 @NonNull 223 private final Clock mClock; 224 private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG); 225 // WARNING : Accessing these values in any thread is not safe, it must only be changed in the 226 // state machine thread. If change this outside state machine, it will need to introduce 227 // synchronization. 228 private boolean mIsDaemonStarted = false; 229 private boolean mIsMonitoringSocketsStarted = false; 230 231 /** 232 * Clients receiving asynchronous messages 233 */ 234 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>(); 235 236 /* A map from transaction(unique) id to client info */ 237 private final SparseArray<ClientInfo> mTransactionIdToClientInfoMap = new SparseArray<>(); 238 239 // Note this is not final to avoid depending on the Wi-Fi service starting before NsdService 240 @Nullable 241 private WifiManager.MulticastLock mHeldMulticastLock; 242 // Fulfilled network requests that require the Wi-Fi lock: key is the obtained Network 243 // (non-null), value is the requested Network (nullable) 244 @NonNull 245 private final ArraySet<Network> mWifiLockRequiredNetworks = new ArraySet<>(); 246 @NonNull 247 private final ArraySet<Integer> mRunningAppActiveUids = new ArraySet<>(); 248 249 private final long mCleanupDelayMs; 250 251 private static final int INVALID_ID = 0; 252 private int mUniqueId = 1; 253 // The count of the connected legacy clients. 254 private int mLegacyClientCount = 0; 255 // The number of client that ever connected. 256 private int mClientNumberId = 1; 257 258 private final RemoteCallbackList<IOffloadEngine> mOffloadEngines = 259 new RemoteCallbackList<>(); 260 @NonNull 261 private final MdnsFeatureFlags mMdnsFeatureFlags; 262 263 private static class OffloadEngineInfo { 264 @NonNull final String mInterfaceName; 265 final long mOffloadCapabilities; 266 final long mOffloadType; 267 @NonNull final IOffloadEngine mOffloadEngine; 268 OffloadEngineInfo(@onNull IOffloadEngine offloadEngine, @NonNull String interfaceName, long capabilities, long offloadType)269 OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine, 270 @NonNull String interfaceName, long capabilities, long offloadType) { 271 this.mOffloadEngine = offloadEngine; 272 this.mInterfaceName = interfaceName; 273 this.mOffloadCapabilities = capabilities; 274 this.mOffloadType = offloadType; 275 } 276 } 277 278 @VisibleForTesting 279 abstract static class MdnsListener implements MdnsServiceBrowserListener { 280 protected final int mClientRequestId; 281 protected final int mTransactionId; 282 @NonNull 283 protected final String mListenedServiceType; 284 MdnsListener(int clientRequestId, int transactionId, @NonNull String listenedServiceType)285 MdnsListener(int clientRequestId, int transactionId, @NonNull String listenedServiceType) { 286 mClientRequestId = clientRequestId; 287 mTransactionId = transactionId; 288 mListenedServiceType = listenedServiceType; 289 } 290 291 @NonNull getListenedServiceType()292 public String getListenedServiceType() { 293 return mListenedServiceType; 294 } 295 296 @Override onServiceFound(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)297 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo, 298 boolean isServiceFromCache) { } 299 300 @Override onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)301 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { } 302 303 @Override onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)304 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { } 305 306 @Override onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)307 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo, 308 boolean isServiceFromCache) { } 309 310 @Override onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)311 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { } 312 313 @Override onSearchStoppedWithError(int error)314 public void onSearchStoppedWithError(int error) { } 315 316 @Override onSearchFailedToStart()317 public void onSearchFailedToStart() { } 318 319 @Override onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)320 public void onDiscoveryQuerySent(@NonNull List<String> subtypes, 321 int sentQueryTransactionId) { } 322 323 @Override onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode)324 public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { } 325 326 // Ensure toString gets overridden 327 @NonNull toString()328 public abstract String toString(); 329 } 330 331 private class DiscoveryListener extends MdnsListener { 332 DiscoveryListener(int clientRequestId, int transactionId, @NonNull String listenServiceType)333 DiscoveryListener(int clientRequestId, int transactionId, 334 @NonNull String listenServiceType) { 335 super(clientRequestId, transactionId, listenServiceType); 336 } 337 338 @Override onServiceNameDiscovered(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)339 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo, 340 boolean isServiceFromCache) { 341 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 342 NsdManager.SERVICE_FOUND, 343 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache)); 344 } 345 346 @Override onServiceNameRemoved(@onNull MdnsServiceInfo serviceInfo)347 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { 348 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 349 NsdManager.SERVICE_LOST, 350 new MdnsEvent(mClientRequestId, serviceInfo)); 351 } 352 353 @Override onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)354 public void onDiscoveryQuerySent(@NonNull List<String> subtypes, 355 int sentQueryTransactionId) { 356 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 357 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId)); 358 } 359 360 @NonNull 361 @Override toString()362 public String toString() { 363 return String.format("DiscoveryListener: serviceType=%s", getListenedServiceType()); 364 } 365 } 366 367 private class ResolutionListener extends MdnsListener { 368 private final String mServiceName; 369 ResolutionListener(int clientRequestId, int transactionId, @NonNull String listenServiceType, @NonNull String serviceName)370 ResolutionListener(int clientRequestId, int transactionId, 371 @NonNull String listenServiceType, @NonNull String serviceName) { 372 super(clientRequestId, transactionId, listenServiceType); 373 mServiceName = serviceName; 374 } 375 376 @Override onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache)377 public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) { 378 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 379 NsdManager.RESOLVE_SERVICE_SUCCEEDED, 380 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache)); 381 } 382 383 @Override onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)384 public void onDiscoveryQuerySent(@NonNull List<String> subtypes, 385 int sentQueryTransactionId) { 386 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 387 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId)); 388 } 389 390 @NonNull 391 @Override toString()392 public String toString() { 393 return String.format("ResolutionListener serviceName=%s, serviceType=%s", 394 mServiceName, getListenedServiceType()); 395 } 396 } 397 398 private class ServiceInfoListener extends MdnsListener { 399 private final String mServiceName; 400 ServiceInfoListener(int clientRequestId, int transactionId, @NonNull String listenServiceType, @NonNull String serviceName)401 ServiceInfoListener(int clientRequestId, int transactionId, 402 @NonNull String listenServiceType, @NonNull String serviceName) { 403 super(clientRequestId, transactionId, listenServiceType); 404 this.mServiceName = serviceName; 405 } 406 407 @Override onServiceFound(@onNull MdnsServiceInfo serviceInfo, boolean isServiceFromCache)408 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo, 409 boolean isServiceFromCache) { 410 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 411 NsdManager.SERVICE_UPDATED, 412 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache)); 413 } 414 415 @Override onServiceUpdated(@onNull MdnsServiceInfo serviceInfo)416 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { 417 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 418 NsdManager.SERVICE_UPDATED, 419 new MdnsEvent(mClientRequestId, serviceInfo)); 420 } 421 422 @Override onServiceRemoved(@onNull MdnsServiceInfo serviceInfo)423 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { 424 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 425 NsdManager.SERVICE_UPDATED_LOST, 426 new MdnsEvent(mClientRequestId, serviceInfo)); 427 } 428 429 @Override onDiscoveryQuerySent(@onNull List<String> subtypes, int sentQueryTransactionId)430 public void onDiscoveryQuerySent(@NonNull List<String> subtypes, 431 int sentQueryTransactionId) { 432 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId, 433 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId)); 434 } 435 436 @NonNull 437 @Override toString()438 public String toString() { 439 return String.format("ServiceInfoListener serviceName=%s, serviceType=%s", 440 mServiceName, getListenedServiceType()); 441 } 442 } 443 444 private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor { 445 @Override onSocketRequestFulfilled(@ullable Network socketNetwork, @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports)446 public void onSocketRequestFulfilled(@Nullable Network socketNetwork, 447 @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) { 448 // The network may be null for Wi-Fi SoftAp interfaces (tethering), but there is no APF 449 // filtering on such interfaces, so taking the multicast lock is not necessary to 450 // disable APF filtering of multicast. 451 if (socketNetwork == null 452 || !CollectionUtils.contains(transports, TRANSPORT_WIFI) 453 || CollectionUtils.contains(transports, TRANSPORT_VPN)) { 454 return; 455 } 456 457 if (mWifiLockRequiredNetworks.add(socketNetwork)) { 458 updateMulticastLock(); 459 } 460 } 461 462 @Override onSocketDestroyed(@ullable Network socketNetwork, @NonNull MdnsInterfaceSocket socket)463 public void onSocketDestroyed(@Nullable Network socketNetwork, 464 @NonNull MdnsInterfaceSocket socket) { 465 if (mWifiLockRequiredNetworks.remove(socketNetwork)) { 466 updateMulticastLock(); 467 } 468 } 469 } 470 471 private class UidImportanceListener implements ActivityManager.OnUidImportanceListener { 472 private final Handler mHandler; 473 UidImportanceListener(Handler handler)474 private UidImportanceListener(Handler handler) { 475 mHandler = handler; 476 } 477 478 @Override onUidImportance(int uid, int importance)479 public void onUidImportance(int uid, int importance) { 480 mHandler.post(() -> handleUidImportanceChanged(uid, importance)); 481 } 482 } 483 handleUidImportanceChanged(int uid, int importance)484 private void handleUidImportanceChanged(int uid, int importance) { 485 // Lower importance values are more "important" 486 final boolean modified = importance <= mRunningAppActiveImportanceCutoff 487 ? mRunningAppActiveUids.add(uid) 488 : mRunningAppActiveUids.remove(uid); 489 if (modified) { 490 updateMulticastLock(); 491 } 492 } 493 494 /** 495 * Take or release the lock based on updated internal state. 496 * 497 * This determines whether the lock needs to be held based on 498 * {@link #mWifiLockRequiredNetworks}, {@link #mRunningAppActiveUids} and 499 * {@link ClientInfo#mClientRequests}, so it must be called after any of the these have been 500 * updated. 501 */ updateMulticastLock()502 private void updateMulticastLock() { 503 final int needsLockUid = getMulticastLockNeededUid(); 504 if (needsLockUid >= 0 && mHeldMulticastLock == null) { 505 final WifiManager wm = mContext.getSystemService(WifiManager.class); 506 if (wm == null) { 507 Log.wtf(TAG, "Got a TRANSPORT_WIFI network without WifiManager"); 508 return; 509 } 510 mHeldMulticastLock = wm.createMulticastLock(TAG); 511 mHeldMulticastLock.acquire(); 512 mServiceLogs.log("Taking multicast lock for uid " + needsLockUid); 513 } else if (needsLockUid < 0 && mHeldMulticastLock != null) { 514 mHeldMulticastLock.release(); 515 mHeldMulticastLock = null; 516 mServiceLogs.log("Released multicast lock"); 517 } 518 } 519 520 /** 521 * @return The UID of an app requiring the multicast lock, or -1 if none. 522 */ getMulticastLockNeededUid()523 private int getMulticastLockNeededUid() { 524 if (mWifiLockRequiredNetworks.size() == 0) { 525 // Return early if NSD is not active, or not on any relevant network 526 return -1; 527 } 528 for (int i = 0; i < mTransactionIdToClientInfoMap.size(); i++) { 529 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.valueAt(i); 530 if (!mRunningAppActiveUids.contains(clientInfo.mUid)) { 531 // Ignore non-active UIDs 532 continue; 533 } 534 535 if (clientInfo.hasAnyJavaBackendRequestForNetworks(mWifiLockRequiredNetworks)) { 536 return clientInfo.mUid; 537 } 538 } 539 return -1; 540 } 541 542 /** 543 * Data class of mdns service callback information. 544 */ 545 private static class MdnsEvent { 546 final int mClientRequestId; 547 @Nullable 548 final MdnsServiceInfo mMdnsServiceInfo; 549 final boolean mIsServiceFromCache; 550 MdnsEvent(int clientRequestId)551 MdnsEvent(int clientRequestId) { 552 this(clientRequestId, null /* mdnsServiceInfo */, false /* isServiceFromCache */); 553 } 554 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo)555 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo) { 556 this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */); 557 } 558 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo, boolean isServiceFromCache)559 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo, 560 boolean isServiceFromCache) { 561 mClientRequestId = clientRequestId; 562 mMdnsServiceInfo = mdnsServiceInfo; 563 mIsServiceFromCache = isServiceFromCache; 564 } 565 } 566 567 // TODO: Use a Handler instead of a StateMachine since there are no state changes. 568 private class NsdStateMachine extends StateMachine { 569 570 private final EnabledState mEnabledState = new EnabledState(); 571 572 @Override getWhatToString(int what)573 protected String getWhatToString(int what) { 574 return NsdManager.nameOf(what); 575 } 576 maybeStartDaemon()577 private void maybeStartDaemon() { 578 if (mIsDaemonStarted) { 579 if (DBG) Log.d(TAG, "Daemon is already started."); 580 return; 581 } 582 583 if (mMDnsManager == null) { 584 Log.wtf(TAG, "maybeStartDaemon: mMDnsManager is null"); 585 return; 586 } 587 mMDnsManager.registerEventListener(mMDnsEventCallback); 588 mMDnsManager.startDaemon(); 589 mIsDaemonStarted = true; 590 maybeScheduleStop(); 591 mServiceLogs.log("Start mdns_responder daemon"); 592 } 593 maybeStopDaemon()594 private void maybeStopDaemon() { 595 if (!mIsDaemonStarted) { 596 if (DBG) Log.d(TAG, "Daemon has not been started."); 597 return; 598 } 599 600 if (mMDnsManager == null) { 601 Log.wtf(TAG, "maybeStopDaemon: mMDnsManager is null"); 602 return; 603 } 604 mMDnsManager.unregisterEventListener(mMDnsEventCallback); 605 mMDnsManager.stopDaemon(); 606 mIsDaemonStarted = false; 607 mServiceLogs.log("Stop mdns_responder daemon"); 608 } 609 isAnyRequestActive()610 private boolean isAnyRequestActive() { 611 return mTransactionIdToClientInfoMap.size() != 0; 612 } 613 scheduleStop()614 private void scheduleStop() { 615 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs); 616 } maybeScheduleStop()617 private void maybeScheduleStop() { 618 // The native daemon should stay alive and can't be cleanup 619 // if any legacy client connected. 620 if (!isAnyRequestActive() && mLegacyClientCount == 0) { 621 scheduleStop(); 622 } 623 } 624 cancelStop()625 private void cancelStop() { 626 this.removeMessages(NsdManager.DAEMON_CLEANUP); 627 } 628 maybeStartMonitoringSockets()629 private void maybeStartMonitoringSockets() { 630 if (mIsMonitoringSocketsStarted) { 631 if (DBG) Log.d(TAG, "Socket monitoring is already started."); 632 return; 633 } 634 635 mMdnsSocketProvider.startMonitoringSockets(); 636 mIsMonitoringSocketsStarted = true; 637 } 638 maybeStopMonitoringSocketsIfNoActiveRequest()639 private void maybeStopMonitoringSocketsIfNoActiveRequest() { 640 if (!mIsMonitoringSocketsStarted) return; 641 if (isAnyRequestActive()) return; 642 643 mMdnsSocketProvider.requestStopWhenInactive(); 644 mIsMonitoringSocketsStarted = false; 645 } 646 NsdStateMachine(String name, Handler handler)647 NsdStateMachine(String name, Handler handler) { 648 super(name, handler); 649 addState(mEnabledState); 650 State initialState = mEnabledState; 651 setInitialState(initialState); 652 setLogRecSize(25); 653 } 654 655 class EnabledState extends State { 656 @Override enter()657 public void enter() { 658 sendNsdStateChangeBroadcast(true); 659 } 660 661 @Override exit()662 public void exit() { 663 // TODO: it is incorrect to stop the daemon without expunging all requests 664 // and sending error callbacks to clients. 665 scheduleStop(); 666 } 667 requestLimitReached(ClientInfo clientInfo)668 private boolean requestLimitReached(ClientInfo clientInfo) { 669 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) { 670 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo); 671 return true; 672 } 673 return false; 674 } 675 storeLegacyRequestMap(int clientRequestId, int transactionId, ClientInfo clientInfo, int what, long startTimeMs)676 private ClientRequest storeLegacyRequestMap(int clientRequestId, int transactionId, 677 ClientInfo clientInfo, int what, long startTimeMs) { 678 final LegacyClientRequest request = 679 new LegacyClientRequest(transactionId, what, startTimeMs); 680 clientInfo.mClientRequests.put(clientRequestId, request); 681 mTransactionIdToClientInfoMap.put(transactionId, clientInfo); 682 // Remove the cleanup event because here comes a new request. 683 cancelStop(); 684 return request; 685 } 686 storeAdvertiserRequestMap(int clientRequestId, int transactionId, ClientInfo clientInfo, @NonNull NsdServiceInfo serviceInfo)687 private void storeAdvertiserRequestMap(int clientRequestId, int transactionId, 688 ClientInfo clientInfo, @NonNull NsdServiceInfo serviceInfo) { 689 final String serviceFullName = 690 serviceInfo.getServiceName() + "." + serviceInfo.getServiceType(); 691 clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest( 692 transactionId, serviceInfo.getNetwork(), serviceFullName, 693 mClock.elapsedRealtime())); 694 mTransactionIdToClientInfoMap.put(transactionId, clientInfo); 695 updateMulticastLock(); 696 } 697 removeRequestMap( int clientRequestId, int transactionId, ClientInfo clientInfo)698 private void removeRequestMap( 699 int clientRequestId, int transactionId, ClientInfo clientInfo) { 700 final ClientRequest existing = clientInfo.mClientRequests.get(clientRequestId); 701 if (existing == null) return; 702 clientInfo.mClientRequests.remove(clientRequestId); 703 mTransactionIdToClientInfoMap.remove(transactionId); 704 705 if (existing instanceof LegacyClientRequest) { 706 maybeScheduleStop(); 707 } else { 708 maybeStopMonitoringSocketsIfNoActiveRequest(); 709 updateMulticastLock(); 710 } 711 } 712 storeDiscoveryManagerRequestMap(int clientRequestId, int transactionId, MdnsListener listener, ClientInfo clientInfo, @Nullable Network requestedNetwork)713 private ClientRequest storeDiscoveryManagerRequestMap(int clientRequestId, 714 int transactionId, MdnsListener listener, ClientInfo clientInfo, 715 @Nullable Network requestedNetwork) { 716 final DiscoveryManagerRequest request = new DiscoveryManagerRequest(transactionId, 717 listener, requestedNetwork, mClock.elapsedRealtime()); 718 clientInfo.mClientRequests.put(clientRequestId, request); 719 mTransactionIdToClientInfoMap.put(transactionId, clientInfo); 720 updateMulticastLock(); 721 return request; 722 } 723 724 /** 725 * Truncate a service name to up to 63 UTF-8 bytes. 726 * 727 * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating 728 * names used in registerService follows historical behavior (see mdnsresponder 729 * handle_regservice_request). 730 */ 731 @NonNull truncateServiceName(@onNull String originalName)732 private String truncateServiceName(@NonNull String originalName) { 733 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH); 734 } 735 stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId, int transactionId, ClientInfo clientInfo)736 private void stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId, 737 int transactionId, ClientInfo clientInfo) { 738 clientInfo.unregisterMdnsListenerFromRequest(request); 739 removeRequestMap(clientRequestId, transactionId, clientInfo); 740 } 741 getClientInfoForReply(Message msg)742 private ClientInfo getClientInfoForReply(Message msg) { 743 final ListenerArgs args = (ListenerArgs) msg.obj; 744 return mClients.get(args.connector); 745 } 746 747 /** 748 * Returns {@code false} if {@code subtypes} exceeds the maximum number limit or 749 * contains invalid subtype label. 750 */ checkSubtypeLabels(Set<String> subtypes)751 private boolean checkSubtypeLabels(Set<String> subtypes) { 752 if (subtypes.size() > MAX_SUBTYPE_COUNT) { 753 mServiceLogs.e( 754 "Too many subtypes: " + subtypes.size() + " (max = " 755 + MAX_SUBTYPE_COUNT + ")"); 756 return false; 757 } 758 759 for (String subtype : subtypes) { 760 if (!checkSubtypeLabel(subtype)) { 761 mServiceLogs.e("Subtype " + subtype + " is invalid"); 762 return false; 763 } 764 } 765 return true; 766 } 767 dedupSubtypeLabels(Collection<String> subtypes)768 private Set<String> dedupSubtypeLabels(Collection<String> subtypes) { 769 final Map<String, String> subtypeMap = new LinkedHashMap<>(subtypes.size()); 770 for (String subtype : subtypes) { 771 subtypeMap.put(MdnsUtils.toDnsLowerCase(subtype), subtype); 772 } 773 return new ArraySet<>(subtypeMap.values()); 774 } 775 checkTtl( @ullable Duration ttl, @NonNull ClientInfo clientInfo)776 private boolean checkTtl( 777 @Nullable Duration ttl, @NonNull ClientInfo clientInfo) { 778 if (ttl == null) { 779 return true; 780 } 781 782 final long ttlSeconds = ttl.toSeconds(); 783 final int uid = clientInfo.getUid(); 784 785 // Allows Thread module in the system_server to register TTL that is smaller than 786 // 30 seconds 787 final long minTtlSeconds = uid == SYSTEM_UID ? 0 : NsdManager.TTL_SECONDS_MIN; 788 789 // Allows Thread module in the system_server to register TTL that is larger than 790 // 10 hours 791 final long maxTtlSeconds = 792 uid == SYSTEM_UID ? 0xffffffffL : NsdManager.TTL_SECONDS_MAX; 793 794 if (ttlSeconds < minTtlSeconds || ttlSeconds > maxTtlSeconds) { 795 mServiceLogs.e("ttlSeconds exceeds allowed range (value = " 796 + ttlSeconds + ", allowedRange = [" + minTtlSeconds 797 + ", " + maxTtlSeconds + " ])"); 798 return false; 799 } 800 return true; 801 } 802 803 @Override processMessage(Message msg)804 public boolean processMessage(Message msg) { 805 final ClientInfo clientInfo; 806 final int transactionId; 807 final int clientRequestId = msg.arg2; 808 final OffloadEngineInfo offloadEngineInfo; 809 switch (msg.what) { 810 case NsdManager.DISCOVER_SERVICES: { 811 if (DBG) Log.d(TAG, "Discover services"); 812 final DiscoveryArgs discoveryArgs = (DiscoveryArgs) msg.obj; 813 clientInfo = mClients.get(discoveryArgs.connector); 814 // If the binder death notification for a INsdManagerCallback was received 815 // before any calls are received by NsdService, the clientInfo would be 816 // cleared and cause NPE. Add a null check here to prevent this corner case. 817 if (clientInfo == null) { 818 Log.e(TAG, "Unknown connector in discovery"); 819 break; 820 } 821 822 if (requestLimitReached(clientInfo)) { 823 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, 824 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */); 825 break; 826 } 827 828 final DiscoveryRequest discoveryRequest = discoveryArgs.discoveryRequest; 829 transactionId = getUniqueId(); 830 final Pair<String, List<String>> typeAndSubtype = 831 parseTypeAndSubtype(discoveryRequest.getServiceType()); 832 final String serviceType = typeAndSubtype == null 833 ? null : typeAndSubtype.first; 834 if (clientInfo.mUseJavaBackend 835 || mDeps.isMdnsDiscoveryManagerEnabled(mContext) 836 || useDiscoveryManagerForType(serviceType)) { 837 if (serviceType == null || typeAndSubtype.second.size() > 1) { 838 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, 839 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); 840 break; 841 } 842 843 String subtype = discoveryRequest.getSubtype(); 844 if (subtype == null && !typeAndSubtype.second.isEmpty()) { 845 subtype = typeAndSubtype.second.get(0); 846 } 847 848 if (subtype != null && !checkSubtypeLabel(subtype)) { 849 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, 850 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); 851 break; 852 } 853 854 final String listenServiceType = serviceType + ".local"; 855 maybeStartMonitoringSockets(); 856 final MdnsListener listener = new DiscoveryListener(clientRequestId, 857 transactionId, listenServiceType); 858 final MdnsSearchOptions.Builder optionsBuilder = 859 MdnsSearchOptions.newBuilder() 860 .setNetwork(discoveryRequest.getNetwork()) 861 .setRemoveExpiredService(true) 862 .setQueryMode( 863 mMdnsFeatureFlags.isAggressiveQueryModeEnabled() 864 ? AGGRESSIVE_QUERY_MODE 865 : PASSIVE_QUERY_MODE); 866 if (subtype != null) { 867 // checkSubtypeLabels() ensures that subtypes start with '_' but 868 // MdnsSearchOptions expects the underscore to not be present. 869 optionsBuilder.addSubtype(subtype.substring(1)); 870 } 871 mMdnsDiscoveryManager.registerListener( 872 listenServiceType, listener, optionsBuilder.build()); 873 final ClientRequest request = storeDiscoveryManagerRequestMap( 874 clientRequestId, transactionId, listener, clientInfo, 875 discoveryRequest.getNetwork()); 876 clientInfo.onDiscoverServicesStarted( 877 clientRequestId, discoveryRequest, request); 878 clientInfo.log("Register a DiscoveryListener " + transactionId 879 + " for service type:" + listenServiceType); 880 } else { 881 maybeStartDaemon(); 882 if (discoverServices(transactionId, discoveryRequest)) { 883 if (DBG) { 884 Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId 885 + discoveryRequest.getServiceType()); 886 } 887 final ClientRequest request = storeLegacyRequestMap(clientRequestId, 888 transactionId, clientInfo, msg.what, 889 mClock.elapsedRealtime()); 890 clientInfo.onDiscoverServicesStarted( 891 clientRequestId, discoveryRequest, request); 892 } else { 893 stopServiceDiscovery(transactionId); 894 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, 895 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); 896 } 897 } 898 break; 899 } 900 case NsdManager.STOP_DISCOVERY: { 901 if (DBG) Log.d(TAG, "Stop service discovery"); 902 final ListenerArgs args = (ListenerArgs) msg.obj; 903 clientInfo = mClients.get(args.connector); 904 // If the binder death notification for a INsdManagerCallback was received 905 // before any calls are received by NsdService, the clientInfo would be 906 // cleared and cause NPE. Add a null check here to prevent this corner case. 907 if (clientInfo == null) { 908 Log.e(TAG, "Unknown connector in stop discovery"); 909 break; 910 } 911 912 final ClientRequest request = 913 clientInfo.mClientRequests.get(clientRequestId); 914 if (request == null) { 915 Log.e(TAG, "Unknown client request in STOP_DISCOVERY"); 916 break; 917 } 918 transactionId = request.mTransactionId; 919 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this 920 // point, so this needs to check the type of the original request to 921 // unregister instead of looking at the flag value. 922 if (request instanceof DiscoveryManagerRequest) { 923 stopDiscoveryManagerRequest( 924 request, clientRequestId, transactionId, clientInfo); 925 clientInfo.onStopDiscoverySucceeded(clientRequestId, request); 926 clientInfo.log("Unregister the DiscoveryListener " + transactionId); 927 } else { 928 removeRequestMap(clientRequestId, transactionId, clientInfo); 929 if (stopServiceDiscovery(transactionId)) { 930 clientInfo.onStopDiscoverySucceeded(clientRequestId, request); 931 } else { 932 clientInfo.onStopDiscoveryFailed( 933 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); 934 } 935 } 936 break; 937 } 938 case NsdManager.REGISTER_SERVICE: { 939 if (DBG) Log.d(TAG, "Register service"); 940 final AdvertisingArgs args = (AdvertisingArgs) msg.obj; 941 clientInfo = mClients.get(args.connector); 942 // If the binder death notification for a INsdManagerCallback was received 943 // before any calls are received by NsdService, the clientInfo would be 944 // cleared and cause NPE. Add a null check here to prevent this corner case. 945 if (clientInfo == null) { 946 Log.e(TAG, "Unknown connector in registration"); 947 break; 948 } 949 950 if (requestLimitReached(clientInfo)) { 951 clientInfo.onRegisterServiceFailedImmediately(clientRequestId, 952 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */); 953 break; 954 } 955 final AdvertisingRequest advertisingRequest = args.advertisingRequest; 956 if (advertisingRequest == null) { 957 Log.e(TAG, "Unknown advertisingRequest in registration"); 958 break; 959 } 960 final NsdServiceInfo serviceInfo = advertisingRequest.getServiceInfo(); 961 final String serviceType = serviceInfo.getServiceType(); 962 final Pair<String, List<String>> typeSubtype = parseTypeAndSubtype( 963 serviceType); 964 final String registerServiceType = typeSubtype == null 965 ? null : typeSubtype.first; 966 final String hostname = serviceInfo.getHostname(); 967 // Keep compatible with the legacy behavior: It's allowed to set host 968 // addresses for a service registration although the host addresses 969 // won't be registered. To register the addresses for a host, the 970 // hostname must be specified. 971 if (hostname == null) { 972 serviceInfo.setHostAddresses(Collections.emptyList()); 973 } 974 if (clientInfo.mUseJavaBackend 975 || mDeps.isMdnsAdvertiserEnabled(mContext) 976 || useAdvertiserForType(registerServiceType)) { 977 if (serviceType != null && registerServiceType == null) { 978 Log.e(TAG, "Invalid service type: " + serviceType); 979 clientInfo.onRegisterServiceFailedImmediately(clientRequestId, 980 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); 981 break; 982 } 983 boolean isUpdateOnly = (advertisingRequest.getAdvertisingConfig() 984 & AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY) > 0; 985 // If it is an update request, then reuse the old transactionId 986 if (isUpdateOnly) { 987 final ClientRequest existingClientRequest = 988 clientInfo.mClientRequests.get(clientRequestId); 989 if (existingClientRequest == null) { 990 Log.e(TAG, "Invalid update on requestId: " + clientRequestId); 991 clientInfo.onRegisterServiceFailedImmediately(clientRequestId, 992 NsdManager.FAILURE_INTERNAL_ERROR, 993 false /* isLegacy */); 994 break; 995 } 996 transactionId = existingClientRequest.mTransactionId; 997 } else { 998 transactionId = getUniqueId(); 999 } 1000 1001 if (registerServiceType != null) { 1002 serviceInfo.setServiceType(registerServiceType); 1003 serviceInfo.setServiceName( 1004 truncateServiceName(serviceInfo.getServiceName())); 1005 } 1006 1007 if (!checkHostname(hostname)) { 1008 clientInfo.onRegisterServiceFailedImmediately(clientRequestId, 1009 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); 1010 break; 1011 } 1012 1013 if (!checkPublicKey(serviceInfo.getPublicKey())) { 1014 Log.e(TAG, 1015 "Invalid public key: " 1016 + Arrays.toString(serviceInfo.getPublicKey())); 1017 clientInfo.onRegisterServiceFailedImmediately( 1018 clientRequestId, 1019 NsdManager.FAILURE_BAD_PARAMETERS, 1020 false /* isLegacy */); 1021 break; 1022 } 1023 1024 Set<String> subtypes = new ArraySet<>(serviceInfo.getSubtypes()); 1025 if (typeSubtype != null && typeSubtype.second != null) { 1026 for (String subType : typeSubtype.second) { 1027 if (!TextUtils.isEmpty(subType)) { 1028 subtypes.add(subType); 1029 } 1030 } 1031 } 1032 subtypes = dedupSubtypeLabels(subtypes); 1033 1034 if (!checkSubtypeLabels(subtypes)) { 1035 clientInfo.onRegisterServiceFailedImmediately(clientRequestId, 1036 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); 1037 break; 1038 } 1039 1040 if (!checkTtl(advertisingRequest.getTtl(), clientInfo)) { 1041 clientInfo.onRegisterServiceFailedImmediately(clientRequestId, 1042 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); 1043 break; 1044 } 1045 1046 serviceInfo.setSubtypes(subtypes); 1047 maybeStartMonitoringSockets(); 1048 final MdnsAdvertisingOptions mdnsAdvertisingOptions = 1049 MdnsAdvertisingOptions.newBuilder() 1050 .setIsOnlyUpdate(isUpdateOnly) 1051 .setTtl(advertisingRequest.getTtl()) 1052 .build(); 1053 mAdvertiser.addOrUpdateService(transactionId, serviceInfo, 1054 mdnsAdvertisingOptions, clientInfo.mUid); 1055 storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo, 1056 serviceInfo); 1057 } else { 1058 maybeStartDaemon(); 1059 transactionId = getUniqueId(); 1060 if (registerService(transactionId, serviceInfo)) { 1061 if (DBG) { 1062 Log.d(TAG, "Register " + clientRequestId 1063 + " " + transactionId); 1064 } 1065 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo, 1066 msg.what, mClock.elapsedRealtime()); 1067 // Return success after mDns reports success 1068 } else { 1069 unregisterService(transactionId); 1070 clientInfo.onRegisterServiceFailedImmediately(clientRequestId, 1071 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); 1072 } 1073 1074 } 1075 break; 1076 } 1077 case NsdManager.UNREGISTER_SERVICE: { 1078 if (DBG) Log.d(TAG, "unregister service"); 1079 final ListenerArgs args = (ListenerArgs) msg.obj; 1080 clientInfo = mClients.get(args.connector); 1081 // If the binder death notification for a INsdManagerCallback was received 1082 // before any calls are received by NsdService, the clientInfo would be 1083 // cleared and cause NPE. Add a null check here to prevent this corner case. 1084 if (clientInfo == null) { 1085 Log.e(TAG, "Unknown connector in unregistration"); 1086 break; 1087 } 1088 final ClientRequest request = 1089 clientInfo.mClientRequests.get(clientRequestId); 1090 if (request == null) { 1091 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE"); 1092 break; 1093 } 1094 transactionId = request.mTransactionId; 1095 removeRequestMap(clientRequestId, transactionId, clientInfo); 1096 1097 // Note isMdnsAdvertiserEnabled may have changed to false at this point, 1098 // so this needs to check the type of the original request to unregister 1099 // instead of looking at the flag value. 1100 if (request instanceof AdvertiserClientRequest) { 1101 final AdvertiserMetrics metrics = 1102 mAdvertiser.getAdvertiserMetrics(transactionId); 1103 mAdvertiser.removeService(transactionId); 1104 clientInfo.onUnregisterServiceSucceeded( 1105 clientRequestId, request, metrics); 1106 } else { 1107 if (unregisterService(transactionId)) { 1108 clientInfo.onUnregisterServiceSucceeded(clientRequestId, request, 1109 new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */, 1110 NO_PACKET /* sentPacketCount */, 1111 0 /* conflictDuringProbingCount */, 1112 0 /* conflictAfterProbingCount */)); 1113 } else { 1114 clientInfo.onUnregisterServiceFailed( 1115 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); 1116 } 1117 } 1118 break; 1119 } 1120 case NsdManager.RESOLVE_SERVICE: { 1121 if (DBG) Log.d(TAG, "Resolve service"); 1122 final ListenerArgs args = (ListenerArgs) msg.obj; 1123 clientInfo = mClients.get(args.connector); 1124 // If the binder death notification for a INsdManagerCallback was received 1125 // before any calls are received by NsdService, the clientInfo would be 1126 // cleared and cause NPE. Add a null check here to prevent this corner case. 1127 if (clientInfo == null) { 1128 Log.e(TAG, "Unknown connector in resolution"); 1129 break; 1130 } 1131 1132 final NsdServiceInfo info = args.serviceInfo; 1133 transactionId = getUniqueId(); 1134 final Pair<String, List<String>> typeSubtype = 1135 parseTypeAndSubtype(info.getServiceType()); 1136 final String serviceType = typeSubtype == null 1137 ? null : typeSubtype.first; 1138 if (clientInfo.mUseJavaBackend 1139 || mDeps.isMdnsDiscoveryManagerEnabled(mContext) 1140 || useDiscoveryManagerForType(serviceType)) { 1141 if (serviceType == null) { 1142 clientInfo.onResolveServiceFailedImmediately(clientRequestId, 1143 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); 1144 break; 1145 } 1146 final String resolveServiceType = serviceType + ".local"; 1147 1148 maybeStartMonitoringSockets(); 1149 final MdnsListener listener = new ResolutionListener(clientRequestId, 1150 transactionId, resolveServiceType, info.getServiceName()); 1151 final int ifaceIdx = info.getNetwork() != null 1152 ? 0 : info.getInterfaceIndex(); 1153 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() 1154 .setNetwork(info.getNetwork()) 1155 .setInterfaceIndex(ifaceIdx) 1156 .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled() 1157 ? AGGRESSIVE_QUERY_MODE 1158 : PASSIVE_QUERY_MODE) 1159 .setResolveInstanceName(info.getServiceName()) 1160 .setRemoveExpiredService(true) 1161 .build(); 1162 mMdnsDiscoveryManager.registerListener( 1163 resolveServiceType, listener, options); 1164 storeDiscoveryManagerRequestMap(clientRequestId, transactionId, 1165 listener, clientInfo, info.getNetwork()); 1166 clientInfo.log("Register a ResolutionListener " + transactionId 1167 + " for service type:" + resolveServiceType); 1168 } else { 1169 if (clientInfo.mResolvedService != null) { 1170 clientInfo.onResolveServiceFailedImmediately(clientRequestId, 1171 NsdManager.FAILURE_ALREADY_ACTIVE, true /* isLegacy */); 1172 break; 1173 } 1174 1175 maybeStartDaemon(); 1176 if (resolveService(transactionId, info)) { 1177 clientInfo.mResolvedService = new NsdServiceInfo(); 1178 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo, 1179 msg.what, mClock.elapsedRealtime()); 1180 } else { 1181 clientInfo.onResolveServiceFailedImmediately(clientRequestId, 1182 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); 1183 } 1184 } 1185 break; 1186 } 1187 case NsdManager.STOP_RESOLUTION: { 1188 if (DBG) Log.d(TAG, "Stop service resolution"); 1189 final ListenerArgs args = (ListenerArgs) msg.obj; 1190 clientInfo = mClients.get(args.connector); 1191 // If the binder death notification for a INsdManagerCallback was received 1192 // before any calls are received by NsdService, the clientInfo would be 1193 // cleared and cause NPE. Add a null check here to prevent this corner case. 1194 if (clientInfo == null) { 1195 Log.e(TAG, "Unknown connector in stop resolution"); 1196 break; 1197 } 1198 1199 final ClientRequest request = 1200 clientInfo.mClientRequests.get(clientRequestId); 1201 if (request == null) { 1202 Log.e(TAG, "Unknown client request in STOP_RESOLUTION"); 1203 break; 1204 } 1205 transactionId = request.mTransactionId; 1206 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this 1207 // point, so this needs to check the type of the original request to 1208 // unregister instead of looking at the flag value. 1209 if (request instanceof DiscoveryManagerRequest) { 1210 stopDiscoveryManagerRequest( 1211 request, clientRequestId, transactionId, clientInfo); 1212 clientInfo.onStopResolutionSucceeded(clientRequestId, request); 1213 clientInfo.log("Unregister the ResolutionListener " + transactionId); 1214 } else { 1215 removeRequestMap(clientRequestId, transactionId, clientInfo); 1216 if (stopResolveService(transactionId)) { 1217 clientInfo.onStopResolutionSucceeded(clientRequestId, request); 1218 } else { 1219 clientInfo.onStopResolutionFailed( 1220 clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING); 1221 } 1222 clientInfo.mResolvedService = null; 1223 } 1224 break; 1225 } 1226 case NsdManager.REGISTER_SERVICE_CALLBACK: { 1227 if (DBG) Log.d(TAG, "Register a service callback"); 1228 final ListenerArgs args = (ListenerArgs) msg.obj; 1229 clientInfo = mClients.get(args.connector); 1230 // If the binder death notification for a INsdManagerCallback was received 1231 // before any calls are received by NsdService, the clientInfo would be 1232 // cleared and cause NPE. Add a null check here to prevent this corner case. 1233 if (clientInfo == null) { 1234 Log.e(TAG, "Unknown connector in callback registration"); 1235 break; 1236 } 1237 1238 final NsdServiceInfo info = args.serviceInfo; 1239 transactionId = getUniqueId(); 1240 final Pair<String, List<String>> typeAndSubtype = 1241 parseTypeAndSubtype(info.getServiceType()); 1242 final String serviceType = typeAndSubtype == null 1243 ? null : typeAndSubtype.first; 1244 if (serviceType == null) { 1245 clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId, 1246 NsdManager.FAILURE_BAD_PARAMETERS); 1247 break; 1248 } 1249 final String resolveServiceType = serviceType + ".local"; 1250 1251 maybeStartMonitoringSockets(); 1252 final MdnsListener listener = new ServiceInfoListener(clientRequestId, 1253 transactionId, resolveServiceType, info.getServiceName()); 1254 final int ifIndex = info.getNetwork() != null 1255 ? 0 : info.getInterfaceIndex(); 1256 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() 1257 .setNetwork(info.getNetwork()) 1258 .setInterfaceIndex(ifIndex) 1259 .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled() 1260 ? AGGRESSIVE_QUERY_MODE 1261 : PASSIVE_QUERY_MODE) 1262 .setResolveInstanceName(info.getServiceName()) 1263 .setRemoveExpiredService(true) 1264 .build(); 1265 mMdnsDiscoveryManager.registerListener( 1266 resolveServiceType, listener, options); 1267 storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener, 1268 clientInfo, info.getNetwork()); 1269 clientInfo.onServiceInfoCallbackRegistered(transactionId); 1270 clientInfo.log("Register a ServiceInfoListener " + transactionId 1271 + " for service type:" + resolveServiceType); 1272 break; 1273 } 1274 case NsdManager.UNREGISTER_SERVICE_CALLBACK: { 1275 if (DBG) Log.d(TAG, "Unregister a service callback"); 1276 final ListenerArgs args = (ListenerArgs) msg.obj; 1277 clientInfo = mClients.get(args.connector); 1278 // If the binder death notification for a INsdManagerCallback was received 1279 // before any calls are received by NsdService, the clientInfo would be 1280 // cleared and cause NPE. Add a null check here to prevent this corner case. 1281 if (clientInfo == null) { 1282 Log.e(TAG, "Unknown connector in callback unregistration"); 1283 break; 1284 } 1285 1286 final ClientRequest request = 1287 clientInfo.mClientRequests.get(clientRequestId); 1288 if (request == null) { 1289 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK"); 1290 break; 1291 } 1292 transactionId = request.mTransactionId; 1293 if (request instanceof DiscoveryManagerRequest) { 1294 stopDiscoveryManagerRequest( 1295 request, clientRequestId, transactionId, clientInfo); 1296 clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request); 1297 clientInfo.log("Unregister the ServiceInfoListener " + transactionId); 1298 } else { 1299 loge("Unregister failed with non-DiscoveryManagerRequest."); 1300 } 1301 break; 1302 } 1303 case MDNS_SERVICE_EVENT: 1304 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) { 1305 return NOT_HANDLED; 1306 } 1307 break; 1308 case MDNS_DISCOVERY_MANAGER_EVENT: 1309 if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) { 1310 return NOT_HANDLED; 1311 } 1312 break; 1313 case NsdManager.REGISTER_OFFLOAD_ENGINE: 1314 offloadEngineInfo = (OffloadEngineInfo) msg.obj; 1315 // TODO: Limits the number of registrations created by a given class. 1316 mOffloadEngines.register(offloadEngineInfo.mOffloadEngine, 1317 offloadEngineInfo); 1318 sendAllOffloadServiceInfos(offloadEngineInfo); 1319 break; 1320 case NsdManager.UNREGISTER_OFFLOAD_ENGINE: 1321 mOffloadEngines.unregister((IOffloadEngine) msg.obj); 1322 break; 1323 case NsdManager.REGISTER_CLIENT: 1324 final ConnectorArgs arg = (ConnectorArgs) msg.obj; 1325 final INsdManagerCallback cb = arg.callback; 1326 try { 1327 cb.asBinder().linkToDeath(arg.connector, 0); 1328 final String tag = "Client" + arg.uid + "-" + mClientNumberId++; 1329 final NetworkNsdReportedMetrics metrics = 1330 mDeps.makeNetworkNsdReportedMetrics( 1331 (int) mClock.elapsedRealtime()); 1332 clientInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend, 1333 mServiceLogs.forSubComponent(tag), metrics); 1334 mClients.put(arg.connector, clientInfo); 1335 } catch (RemoteException e) { 1336 Log.w(TAG, "Client request id " + clientRequestId 1337 + " has already died"); 1338 } 1339 break; 1340 case NsdManager.UNREGISTER_CLIENT: 1341 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj; 1342 clientInfo = mClients.remove(connector); 1343 if (clientInfo != null) { 1344 clientInfo.expungeAllRequests(); 1345 if (clientInfo.isPreSClient()) { 1346 mLegacyClientCount -= 1; 1347 } 1348 } 1349 maybeStopMonitoringSocketsIfNoActiveRequest(); 1350 maybeScheduleStop(); 1351 break; 1352 case NsdManager.DAEMON_CLEANUP: 1353 maybeStopDaemon(); 1354 break; 1355 // This event should be only sent by the legacy (target SDK < S) clients. 1356 // Mark the sending client as legacy. 1357 case NsdManager.DAEMON_STARTUP: 1358 clientInfo = getClientInfoForReply(msg); 1359 if (clientInfo != null) { 1360 cancelStop(); 1361 clientInfo.setPreSClient(); 1362 mLegacyClientCount += 1; 1363 maybeStartDaemon(); 1364 } 1365 break; 1366 default: 1367 Log.wtf(TAG, "Unhandled " + msg); 1368 return NOT_HANDLED; 1369 } 1370 return HANDLED; 1371 } 1372 handleMDnsServiceEvent(int code, int transactionId, Object obj)1373 private boolean handleMDnsServiceEvent(int code, int transactionId, Object obj) { 1374 NsdServiceInfo servInfo; 1375 ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId); 1376 if (clientInfo == null) { 1377 Log.e(TAG, String.format( 1378 "transactionId %d for %d has no client mapping", transactionId, code)); 1379 return false; 1380 } 1381 1382 /* This goes in response as msg.arg2 */ 1383 int clientRequestId = clientInfo.getClientRequestId(transactionId); 1384 if (clientRequestId < 0) { 1385 // This can happen because of race conditions. For example, 1386 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY, 1387 // and we may get in this situation. 1388 Log.d(TAG, String.format("%d for transactionId %d that is no longer active", 1389 code, transactionId)); 1390 return false; 1391 } 1392 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId); 1393 if (request == null) { 1394 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId); 1395 return false; 1396 } 1397 if (DBG) { 1398 Log.d(TAG, String.format( 1399 "MDns service event code:%d transactionId=%d", code, transactionId)); 1400 } 1401 switch (code) { 1402 case IMDnsEventListener.SERVICE_FOUND: { 1403 final DiscoveryInfo info = (DiscoveryInfo) obj; 1404 final String name = info.serviceName; 1405 final String type = info.registrationType; 1406 servInfo = new NsdServiceInfo(name, type); 1407 final int foundNetId = info.netId; 1408 if (foundNetId == 0L) { 1409 // Ignore services that do not have a Network: they are not usable 1410 // by apps, as they would need privileged permissions to use 1411 // interfaces that do not have an associated Network. 1412 break; 1413 } 1414 if (foundNetId == INetd.DUMMY_NET_ID) { 1415 // Ignore services on the dummy0 interface: they are only seen when 1416 // discovering locally advertised services, and are not reachable 1417 // through that interface. 1418 break; 1419 } 1420 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx); 1421 1422 clientInfo.onServiceFound(clientRequestId, servInfo, request); 1423 break; 1424 } 1425 case IMDnsEventListener.SERVICE_LOST: { 1426 final DiscoveryInfo info = (DiscoveryInfo) obj; 1427 final String name = info.serviceName; 1428 final String type = info.registrationType; 1429 final int lostNetId = info.netId; 1430 servInfo = new NsdServiceInfo(name, type); 1431 // The network could be set to null (netId 0) if it was torn down when the 1432 // service is lost 1433 // TODO: avoid returning null in that case, possibly by remembering 1434 // found services on the same interface index and their network at the time 1435 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx); 1436 clientInfo.onServiceLost(clientRequestId, servInfo, request); 1437 break; 1438 } 1439 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED: 1440 clientInfo.onDiscoverServicesFailed(clientRequestId, 1441 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, 1442 transactionId, 1443 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 1444 break; 1445 case IMDnsEventListener.SERVICE_REGISTERED: { 1446 final RegistrationInfo info = (RegistrationInfo) obj; 1447 final String name = info.serviceName; 1448 servInfo = new NsdServiceInfo(name, null /* serviceType */); 1449 clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, request); 1450 break; 1451 } 1452 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED: 1453 clientInfo.onRegisterServiceFailed(clientRequestId, 1454 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, 1455 transactionId, 1456 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 1457 break; 1458 case IMDnsEventListener.SERVICE_RESOLVED: { 1459 final ResolutionInfo info = (ResolutionInfo) obj; 1460 int index = 0; 1461 final String fullName = info.serviceFullName; 1462 while (index < fullName.length() && fullName.charAt(index) != '.') { 1463 if (fullName.charAt(index) == '\\') { 1464 ++index; 1465 } 1466 ++index; 1467 } 1468 if (index >= fullName.length()) { 1469 Log.e(TAG, "Invalid service found " + fullName); 1470 break; 1471 } 1472 1473 String name = unescape(fullName.substring(0, index)); 1474 String rest = fullName.substring(index); 1475 String type = rest.replace(".local.", ""); 1476 1477 final NsdServiceInfo serviceInfo = clientInfo.mResolvedService; 1478 serviceInfo.setServiceName(name); 1479 serviceInfo.setServiceType(type); 1480 serviceInfo.setPort(info.port); 1481 serviceInfo.setTxtRecords(info.txtRecord); 1482 // Network will be added after SERVICE_GET_ADDR_SUCCESS 1483 1484 stopResolveService(transactionId); 1485 removeRequestMap(clientRequestId, transactionId, clientInfo); 1486 1487 final int transactionId2 = getUniqueId(); 1488 if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) { 1489 storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo, 1490 NsdManager.RESOLVE_SERVICE, request.mStartTimeMs); 1491 } else { 1492 clientInfo.onResolveServiceFailed(clientRequestId, 1493 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, 1494 transactionId, 1495 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 1496 clientInfo.mResolvedService = null; 1497 } 1498 break; 1499 } 1500 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED: 1501 /* NNN resolveId errorCode */ 1502 stopResolveService(transactionId); 1503 removeRequestMap(clientRequestId, transactionId, clientInfo); 1504 clientInfo.onResolveServiceFailed(clientRequestId, 1505 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, 1506 transactionId, 1507 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 1508 clientInfo.mResolvedService = null; 1509 break; 1510 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED: 1511 /* NNN resolveId errorCode */ 1512 stopGetAddrInfo(transactionId); 1513 removeRequestMap(clientRequestId, transactionId, clientInfo); 1514 clientInfo.onResolveServiceFailed(clientRequestId, 1515 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, 1516 transactionId, 1517 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 1518 clientInfo.mResolvedService = null; 1519 break; 1520 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: { 1521 /* NNN resolveId hostname ttl addr interfaceIdx netId */ 1522 final GetAddressInfo info = (GetAddressInfo) obj; 1523 final String address = info.address; 1524 final int netId = info.netId; 1525 InetAddress serviceHost = null; 1526 try { 1527 serviceHost = InetAddress.getByName(address); 1528 } catch (UnknownHostException e) { 1529 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e); 1530 } 1531 1532 // If the resolved service is on an interface without a network, consider it 1533 // as a failure: it would not be usable by apps as they would need 1534 // privileged permissions. 1535 if (netId != NETID_UNSET && serviceHost != null) { 1536 clientInfo.mResolvedService.setHost(serviceHost); 1537 setServiceNetworkForCallback(clientInfo.mResolvedService, 1538 netId, info.interfaceIdx); 1539 clientInfo.onResolveServiceSucceeded( 1540 clientRequestId, clientInfo.mResolvedService, request); 1541 } else { 1542 clientInfo.onResolveServiceFailed(clientRequestId, 1543 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, 1544 transactionId, 1545 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 1546 } 1547 stopGetAddrInfo(transactionId); 1548 removeRequestMap(clientRequestId, transactionId, clientInfo); 1549 clientInfo.mResolvedService = null; 1550 break; 1551 } 1552 default: 1553 return false; 1554 } 1555 return true; 1556 } 1557 1558 @Nullable buildNsdServiceInfoFromMdnsEvent( final MdnsEvent event, int code)1559 private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent( 1560 final MdnsEvent event, int code) { 1561 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; 1562 final String[] typeArray = serviceInfo.getServiceType(); 1563 final String joinedType; 1564 if (typeArray.length == 0 1565 || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) { 1566 Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: " 1567 + Arrays.toString(typeArray)); 1568 return null; 1569 } else { 1570 joinedType = TextUtils.join(".", 1571 Arrays.copyOfRange(typeArray, 0, typeArray.length - 1)); 1572 } 1573 final String serviceType; 1574 switch (code) { 1575 case NsdManager.SERVICE_FOUND: 1576 case NsdManager.SERVICE_LOST: 1577 // For consistency with historical behavior, discovered service types have 1578 // a dot at the end. 1579 serviceType = joinedType + "."; 1580 break; 1581 case RESOLVE_SERVICE_SUCCEEDED: 1582 // For consistency with historical behavior, resolved service types have 1583 // a dot at the beginning. 1584 serviceType = "." + joinedType; 1585 break; 1586 default: 1587 serviceType = joinedType; 1588 break; 1589 } 1590 final String serviceName = serviceInfo.getServiceInstanceName(); 1591 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType); 1592 final Network network = serviceInfo.getNetwork(); 1593 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a 1594 // network for Tethering interface. In other words, the network == null means the 1595 // network has netId = INetd.LOCAL_NET_ID. 1596 setServiceNetworkForCallback( 1597 servInfo, 1598 network == null ? INetd.LOCAL_NET_ID : network.netId, 1599 serviceInfo.getInterfaceIndex()); 1600 servInfo.setSubtypes(dedupSubtypeLabels(serviceInfo.getSubtypes())); 1601 servInfo.setExpirationTime(serviceInfo.getExpirationTime()); 1602 return servInfo; 1603 } 1604 handleMdnsDiscoveryManagerEvent( int transactionId, int code, Object obj)1605 private boolean handleMdnsDiscoveryManagerEvent( 1606 int transactionId, int code, Object obj) { 1607 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId); 1608 if (clientInfo == null) { 1609 Log.e(TAG, String.format( 1610 "id %d for %d has no client mapping", transactionId, code)); 1611 return false; 1612 } 1613 1614 final MdnsEvent event = (MdnsEvent) obj; 1615 final int clientRequestId = event.mClientRequestId; 1616 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId); 1617 if (request == null) { 1618 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId); 1619 return false; 1620 } 1621 1622 // Deal with the discovery sent callback 1623 if (code == DISCOVERY_QUERY_SENT_CALLBACK) { 1624 request.onQuerySent(); 1625 return true; 1626 } 1627 1628 // Deal with other callbacks. 1629 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code); 1630 // Errors are already logged if null 1631 if (info == null) return false; 1632 mServiceLogs.log(String.format( 1633 "MdnsDiscoveryManager event code=%s transactionId=%d", 1634 NsdManager.nameOf(code), transactionId)); 1635 switch (code) { 1636 case NsdManager.SERVICE_FOUND: 1637 // Set the ServiceFromCache flag only if the service is actually being 1638 // retrieved from the cache. This flag should not be overridden by later 1639 // service found event, which may not be cached. 1640 if (event.mIsServiceFromCache) { 1641 request.setServiceFromCache(true); 1642 } 1643 clientInfo.onServiceFound(clientRequestId, info, request); 1644 break; 1645 case NsdManager.SERVICE_LOST: 1646 clientInfo.onServiceLost(clientRequestId, info, request); 1647 break; 1648 case NsdManager.RESOLVE_SERVICE_SUCCEEDED: { 1649 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; 1650 info.setPort(serviceInfo.getPort()); 1651 1652 Map<String, String> attrs = serviceInfo.getAttributes(); 1653 for (Map.Entry<String, String> kv : attrs.entrySet()) { 1654 final String key = kv.getKey(); 1655 try { 1656 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); 1657 } catch (IllegalArgumentException e) { 1658 Log.e(TAG, "Invalid attribute", e); 1659 } 1660 } 1661 info.setHostname(getHostname(serviceInfo)); 1662 final List<InetAddress> addresses = getInetAddresses(serviceInfo); 1663 if (addresses.size() != 0) { 1664 info.setHostAddresses(addresses); 1665 request.setServiceFromCache(event.mIsServiceFromCache); 1666 clientInfo.onResolveServiceSucceeded(clientRequestId, info, request); 1667 } else { 1668 // No address. Notify resolution failure. 1669 clientInfo.onResolveServiceFailed(clientRequestId, 1670 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */, 1671 transactionId, 1672 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 1673 } 1674 1675 // Unregister the listener immediately like IMDnsEventListener design 1676 if (!(request instanceof DiscoveryManagerRequest)) { 1677 Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event"); 1678 break; 1679 } 1680 stopDiscoveryManagerRequest( 1681 request, clientRequestId, transactionId, clientInfo); 1682 break; 1683 } 1684 case NsdManager.SERVICE_UPDATED: { 1685 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; 1686 info.setPort(serviceInfo.getPort()); 1687 1688 Map<String, String> attrs = serviceInfo.getAttributes(); 1689 for (Map.Entry<String, String> kv : attrs.entrySet()) { 1690 final String key = kv.getKey(); 1691 try { 1692 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); 1693 } catch (IllegalArgumentException e) { 1694 Log.e(TAG, "Invalid attribute", e); 1695 } 1696 } 1697 1698 info.setHostname(getHostname(serviceInfo)); 1699 final List<InetAddress> addresses = getInetAddresses(serviceInfo); 1700 info.setHostAddresses(addresses); 1701 clientInfo.onServiceUpdated(clientRequestId, info, request); 1702 // Set the ServiceFromCache flag only if the service is actually being 1703 // retrieved from the cache. This flag should not be overridden by later 1704 // service updates, which may not be cached. 1705 if (event.mIsServiceFromCache) { 1706 request.setServiceFromCache(true); 1707 } 1708 break; 1709 } 1710 case NsdManager.SERVICE_UPDATED_LOST: 1711 clientInfo.onServiceUpdatedLost(clientRequestId, request); 1712 break; 1713 default: 1714 return false; 1715 } 1716 return true; 1717 } 1718 } 1719 } 1720 1721 @NonNull getInetAddresses(@onNull MdnsServiceInfo serviceInfo)1722 private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) { 1723 final List<String> v4Addrs = serviceInfo.getIpv4Addresses(); 1724 final List<String> v6Addrs = serviceInfo.getIpv6Addresses(); 1725 final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size()); 1726 for (String ipv4Address : v4Addrs) { 1727 try { 1728 addresses.add(InetAddresses.parseNumericAddress(ipv4Address)); 1729 } catch (IllegalArgumentException e) { 1730 Log.wtf(TAG, "Invalid ipv4 address", e); 1731 } 1732 } 1733 for (String ipv6Address : v6Addrs) { 1734 try { 1735 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress( 1736 ipv6Address); 1737 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex())); 1738 } catch (IllegalArgumentException e) { 1739 Log.wtf(TAG, "Invalid ipv6 address", e); 1740 } 1741 } 1742 return addresses; 1743 } 1744 1745 @NonNull getHostname(@onNull MdnsServiceInfo serviceInfo)1746 private static String getHostname(@NonNull MdnsServiceInfo serviceInfo) { 1747 String[] hostname = serviceInfo.getHostName(); 1748 // Strip the "local" top-level domain. 1749 if (hostname.length >= 2 && hostname[hostname.length - 1].equals("local")) { 1750 hostname = Arrays.copyOf(hostname, hostname.length - 1); 1751 } 1752 return String.join(".", hostname); 1753 } 1754 setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx)1755 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) { 1756 switch (netId) { 1757 case NETID_UNSET: 1758 info.setNetwork(null); 1759 break; 1760 case INetd.LOCAL_NET_ID: 1761 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally 1762 // visible / usable for apps, so do not return it. Store the interface 1763 // index instead, so at least if the client tries to resolve the service 1764 // with that NsdServiceInfo, it will be done on the same interface. 1765 // If they recreate the NsdServiceInfo themselves, resolution would be 1766 // done on all interfaces as before T, which should also work. 1767 info.setNetwork(null); 1768 info.setInterfaceIndex(ifaceIdx); 1769 break; 1770 default: 1771 info.setNetwork(new Network(netId)); 1772 } 1773 } 1774 1775 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable 1776 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name 1777 // unescape for getting right service address. See "Notes on DNS Name Escaping" on 1778 // external/mdnsresponder/mDNSShared/dns_sd.h for more details. unescape(String s)1779 private String unescape(String s) { 1780 StringBuilder sb = new StringBuilder(s.length()); 1781 for (int i = 0; i < s.length(); ++i) { 1782 char c = s.charAt(i); 1783 if (c == '\\') { 1784 if (++i >= s.length()) { 1785 Log.e(TAG, "Unexpected end of escape sequence in: " + s); 1786 break; 1787 } 1788 c = s.charAt(i); 1789 if (c != '.' && c != '\\') { 1790 if (i + 2 >= s.length()) { 1791 Log.e(TAG, "Unexpected end of escape sequence in: " + s); 1792 break; 1793 } 1794 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10 1795 + (s.charAt(i + 2) - '0')); 1796 i += 2; 1797 } 1798 } 1799 sb.append(c); 1800 } 1801 return sb.toString(); 1802 } 1803 1804 /** 1805 * Check the given service type is valid and construct it to a service type 1806 * which can use for discovery / resolution service. 1807 * 1808 * <p>The valid service type should be 2 labels, or 3 labels if the query is for a 1809 * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an 1810 * underscore; they are alphanumerical characters or dashes or underscore, except the 1811 * last one that is just alphanumerical. The last label must be _tcp or _udp. 1812 * 1813 * <p>The subtypes may also be specified with a comma after the service type, for example 1814 * _type._tcp,_subtype1,_subtype2 1815 * 1816 * @param serviceType the request service type for discovery / resolution service 1817 * @return constructed service type or null if the given service type is invalid. 1818 */ 1819 @Nullable parseTypeAndSubtype(String serviceType)1820 public static Pair<String, List<String>> parseTypeAndSubtype(String serviceType) { 1821 if (TextUtils.isEmpty(serviceType)) return null; 1822 final Pattern serviceTypePattern = Pattern.compile(TYPE_REGEX); 1823 final Matcher matcher = serviceTypePattern.matcher(serviceType); 1824 if (!matcher.matches()) return null; 1825 final String queryType = matcher.group(2); 1826 // Use the subtype at the beginning 1827 if (matcher.group(1) != null) { 1828 return new Pair<>(queryType, List.of(matcher.group(1))); 1829 } 1830 // Use the subtypes at the end 1831 final String subTypesStr = matcher.group(3); 1832 if (subTypesStr != null && !subTypesStr.isEmpty()) { 1833 final String[] subTypes = subTypesStr.substring(1).split(","); 1834 return new Pair<>(queryType, List.of(subTypes)); 1835 } 1836 1837 return new Pair<>(queryType, Collections.emptyList()); 1838 } 1839 1840 /** 1841 * Checks if the hostname is valid. 1842 * 1843 * <p>For now NsdService only allows single-label hostnames conforming to RFC 1035. In other 1844 * words, the hostname should be at most 63 characters long and it only contains letters, digits 1845 * and hyphens. 1846 * 1847 * <p>Additionally, this allows hostname starting with a digit to support Matter devices. Per 1848 * Matter spec 4.3.1.1: 1849 * 1850 * <p>The target host name SHALL be constructed using one of the available link-layer addresses, 1851 * such as a 48-bit device MAC address (for Ethernet and Wi‑Fi) or a 64-bit MAC Extended Address 1852 * (for Thread) expressed as a fixed-length twelve-character (or sixteen-character) hexadecimal 1853 * string, encoded as ASCII (UTF-8) text using capital letters, e.g., B75AFB458ECD.<domain>. 1854 */ checkHostname(@ullable String hostname)1855 public static boolean checkHostname(@Nullable String hostname) { 1856 if (hostname == null) { 1857 return true; 1858 } 1859 String HOSTNAME_REGEX = "^[a-zA-Z0-9]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$"; 1860 return Pattern.compile(HOSTNAME_REGEX).matcher(hostname).matches(); 1861 } 1862 1863 /** 1864 * Checks if the public key is valid. 1865 * 1866 * <p>For simplicity, it only checks if the protocol is DNSSEC and the RDATA is not fewer than 4 1867 * bytes. See RFC 3445 Section 3. 1868 * 1869 * <p>Message format: flags (2 bytes), protocol (1 byte), algorithm (1 byte), public key. 1870 */ checkPublicKey(@ullable byte[] publicKey)1871 private static boolean checkPublicKey(@Nullable byte[] publicKey) { 1872 if (publicKey == null) { 1873 return true; 1874 } 1875 if (publicKey.length < 4) { 1876 return false; 1877 } 1878 int protocol = publicKey[2]; 1879 return protocol == DNSSEC_PROTOCOL; 1880 } 1881 1882 /** Returns {@code true} if {@code subtype} is a valid DNS-SD subtype label. */ checkSubtypeLabel(String subtype)1883 private static boolean checkSubtypeLabel(String subtype) { 1884 return Pattern.compile("^" + SUBTYPE_LABEL_REGEX + "$").matcher(subtype).matches(); 1885 } 1886 1887 @VisibleForTesting NsdService(Context ctx, Handler handler, long cleanupDelayMs)1888 NsdService(Context ctx, Handler handler, long cleanupDelayMs) { 1889 this(ctx, handler, cleanupDelayMs, new Dependencies()); 1890 } 1891 1892 @VisibleForTesting NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps)1893 NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) { 1894 mCleanupDelayMs = cleanupDelayMs; 1895 mContext = ctx; 1896 mNsdStateMachine = new NsdStateMachine(TAG, handler); 1897 mNsdStateMachine.start(); 1898 // It can fail on V+ device since mdns native service provided by netd is removed. 1899 mMDnsManager = SdkLevel.isAtLeastV() ? null : ctx.getSystemService(MDnsManager.class); 1900 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine); 1901 mDeps = deps; 1902 1903 mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(), 1904 LOGGER.forSubComponent("MdnsSocketProvider"), new SocketRequestMonitor()); 1905 // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all 1906 // address events are received. When the netlink monitor starts, any IP addresses already 1907 // on the interfaces will not be seen. In practice, the network will not connect at boot 1908 // time As a result, all the netlink message should be observed if the netlink monitor 1909 // starts here. 1910 handler.post(mMdnsSocketProvider::startNetLinkMonitor); 1911 1912 // NsdService is started after ActivityManager (startOtherServices in SystemServer, vs. 1913 // startBootstrapServices). 1914 mRunningAppActiveImportanceCutoff = mDeps.getDeviceConfigInt( 1915 MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF, 1916 DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF); 1917 final ActivityManager am = ctx.getSystemService(ActivityManager.class); 1918 am.addOnUidImportanceListener(new UidImportanceListener(handler), 1919 mRunningAppActiveImportanceCutoff); 1920 1921 mMdnsFeatureFlags = new MdnsFeatureFlags.Builder() 1922 .setIsMdnsOffloadFeatureEnabled(mDeps.isTetheringFeatureNotChickenedOut( 1923 mContext, MdnsFeatureFlags.NSD_FORCE_DISABLE_MDNS_OFFLOAD)) 1924 .setIncludeInetAddressRecordsInProbing(mDeps.isFeatureEnabled( 1925 mContext, MdnsFeatureFlags.INCLUDE_INET_ADDRESS_RECORDS_IN_PROBING)) 1926 .setIsExpiredServicesRemovalEnabled(mDeps.isTetheringFeatureNotChickenedOut( 1927 mContext, MdnsFeatureFlags.NSD_EXPIRED_SERVICES_REMOVAL)) 1928 .setIsLabelCountLimitEnabled(mDeps.isTetheringFeatureNotChickenedOut( 1929 mContext, MdnsFeatureFlags.NSD_LIMIT_LABEL_COUNT)) 1930 .setIsKnownAnswerSuppressionEnabled(mDeps.isTetheringFeatureNotChickenedOut( 1931 mContext, MdnsFeatureFlags.NSD_KNOWN_ANSWER_SUPPRESSION)) 1932 .setIsUnicastReplyEnabled(mDeps.isTetheringFeatureNotChickenedOut( 1933 mContext, MdnsFeatureFlags.NSD_UNICAST_REPLY_ENABLED)) 1934 .setIsAggressiveQueryModeEnabled(mDeps.isFeatureEnabled( 1935 mContext, MdnsFeatureFlags.NSD_AGGRESSIVE_QUERY_MODE)) 1936 .setIsQueryWithKnownAnswerEnabled(mDeps.isFeatureEnabled( 1937 mContext, MdnsFeatureFlags.NSD_QUERY_WITH_KNOWN_ANSWER)) 1938 .setOverrideProvider(flag -> mDeps.isFeatureEnabled( 1939 mContext, FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag)) 1940 .build(); 1941 mMdnsSocketClient = 1942 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider, 1943 LOGGER.forSubComponent("MdnsMultinetworkSocketClient"), mMdnsFeatureFlags); 1944 mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(), 1945 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"), 1946 mMdnsFeatureFlags); 1947 handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager)); 1948 mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider, 1949 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"), 1950 mMdnsFeatureFlags, mContext); 1951 mClock = deps.makeClock(); 1952 } 1953 1954 /** 1955 * Dependencies of NsdService, for injection in tests. 1956 */ 1957 @VisibleForTesting 1958 public static class Dependencies { 1959 /** 1960 * Check whether the MdnsDiscoveryManager feature is enabled. 1961 * 1962 * @param context The global context information about an app environment. 1963 * @return true if the MdnsDiscoveryManager feature is enabled. 1964 */ isMdnsDiscoveryManagerEnabled(Context context)1965 public boolean isMdnsDiscoveryManagerEnabled(Context context) { 1966 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context, 1967 MDNS_DISCOVERY_MANAGER_VERSION); 1968 } 1969 1970 /** 1971 * Check whether the MdnsAdvertiser feature is enabled. 1972 * 1973 * @param context The global context information about an app environment. 1974 * @return true if the MdnsAdvertiser feature is enabled. 1975 */ isMdnsAdvertiserEnabled(Context context)1976 public boolean isMdnsAdvertiserEnabled(Context context) { 1977 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context, 1978 MDNS_ADVERTISER_VERSION); 1979 } 1980 1981 /** 1982 * Get the type allowlist flag value. 1983 * @see #MDNS_TYPE_ALLOWLIST_FLAGS 1984 */ 1985 @Nullable getTypeAllowlistFlags()1986 public String getTypeAllowlistFlags() { 1987 return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING, 1988 MDNS_TYPE_ALLOWLIST_FLAGS, null); 1989 } 1990 1991 /** 1992 * @see DeviceConfigUtils#isTetheringFeatureEnabled 1993 */ isFeatureEnabled(Context context, String feature)1994 public boolean isFeatureEnabled(Context context, String feature) { 1995 return DeviceConfigUtils.isTetheringFeatureEnabled(context, feature); 1996 } 1997 1998 /** 1999 * @see DeviceConfigUtils#isTetheringFeatureNotChickenedOut 2000 */ isTetheringFeatureNotChickenedOut(Context context, String feature)2001 public boolean isTetheringFeatureNotChickenedOut(Context context, String feature) { 2002 return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, feature); 2003 } 2004 2005 /** 2006 * @see MdnsDiscoveryManager 2007 */ makeMdnsDiscoveryManager( @onNull ExecutorProvider executorProvider, @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog, @NonNull MdnsFeatureFlags featureFlags)2008 public MdnsDiscoveryManager makeMdnsDiscoveryManager( 2009 @NonNull ExecutorProvider executorProvider, 2010 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog, 2011 @NonNull MdnsFeatureFlags featureFlags) { 2012 return new MdnsDiscoveryManager( 2013 executorProvider, socketClient, sharedLog, featureFlags); 2014 } 2015 2016 /** 2017 * @see MdnsAdvertiser 2018 */ makeMdnsAdvertiser( @onNull Looper looper, @NonNull MdnsSocketProvider socketProvider, @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog, MdnsFeatureFlags featureFlags, Context context)2019 public MdnsAdvertiser makeMdnsAdvertiser( 2020 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider, 2021 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog, 2022 MdnsFeatureFlags featureFlags, Context context) { 2023 return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog, featureFlags, context); 2024 } 2025 2026 /** 2027 * @see MdnsSocketProvider 2028 */ makeMdnsSocketProvider(@onNull Context context, @NonNull Looper looper, @NonNull SharedLog sharedLog, @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback)2029 public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context, 2030 @NonNull Looper looper, @NonNull SharedLog sharedLog, 2031 @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback) { 2032 return new MdnsSocketProvider(context, looper, sharedLog, socketCreationCallback); 2033 } 2034 2035 /** 2036 * @see DeviceConfig#getInt(String, String, int) 2037 */ getDeviceConfigInt(@onNull String config, int defaultValue)2038 public int getDeviceConfigInt(@NonNull String config, int defaultValue) { 2039 return DeviceConfig.getInt(NAMESPACE_TETHERING, config, defaultValue); 2040 } 2041 2042 /** 2043 * @see Binder#getCallingUid() 2044 */ getCallingUid()2045 public int getCallingUid() { 2046 return Binder.getCallingUid(); 2047 } 2048 2049 /** 2050 * @see NetworkNsdReportedMetrics 2051 */ makeNetworkNsdReportedMetrics(int clientId)2052 public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics(int clientId) { 2053 return new NetworkNsdReportedMetrics(clientId); 2054 } 2055 2056 /** 2057 * @see MdnsUtils.Clock 2058 */ makeClock()2059 public Clock makeClock() { 2060 return new Clock(); 2061 } 2062 } 2063 2064 /** 2065 * Return whether a type is allowlisted to use the Java backend. 2066 * @param type The service type 2067 * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or 2068 * {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}. 2069 */ isTypeAllowlistedForJavaBackend(@ullable String type, @NonNull String flagPrefix)2070 private boolean isTypeAllowlistedForJavaBackend(@Nullable String type, 2071 @NonNull String flagPrefix) { 2072 if (type == null) return false; 2073 final String typesConfig = mDeps.getTypeAllowlistFlags(); 2074 if (TextUtils.isEmpty(typesConfig)) return false; 2075 2076 final String mappingPrefix = type + ":"; 2077 String mappedFlag = null; 2078 for (String mapping : TextUtils.split(typesConfig, ",")) { 2079 if (mapping.startsWith(mappingPrefix)) { 2080 mappedFlag = mapping.substring(mappingPrefix.length()); 2081 break; 2082 } 2083 } 2084 2085 if (mappedFlag == null) return false; 2086 2087 return mDeps.isFeatureEnabled(mContext, 2088 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX); 2089 } 2090 useDiscoveryManagerForType(@ullable String type)2091 private boolean useDiscoveryManagerForType(@Nullable String type) { 2092 return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX); 2093 } 2094 useAdvertiserForType(@ullable String type)2095 private boolean useAdvertiserForType(@Nullable String type) { 2096 return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX); 2097 } 2098 create(Context context)2099 public static NsdService create(Context context) { 2100 HandlerThread thread = new HandlerThread(TAG); 2101 thread.start(); 2102 Handler handler = new Handler(thread.getLooper()); 2103 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS); 2104 return service; 2105 } 2106 2107 private static class MDnsEventCallback extends IMDnsEventListener.Stub { 2108 private final StateMachine mStateMachine; 2109 MDnsEventCallback(StateMachine sm)2110 MDnsEventCallback(StateMachine sm) { 2111 mStateMachine = sm; 2112 } 2113 2114 @Override onServiceRegistrationStatus(final RegistrationInfo status)2115 public void onServiceRegistrationStatus(final RegistrationInfo status) { 2116 mStateMachine.sendMessage( 2117 MDNS_SERVICE_EVENT, status.result, status.id, status); 2118 } 2119 2120 @Override onServiceDiscoveryStatus(final DiscoveryInfo status)2121 public void onServiceDiscoveryStatus(final DiscoveryInfo status) { 2122 mStateMachine.sendMessage( 2123 MDNS_SERVICE_EVENT, status.result, status.id, status); 2124 } 2125 2126 @Override onServiceResolutionStatus(final ResolutionInfo status)2127 public void onServiceResolutionStatus(final ResolutionInfo status) { 2128 mStateMachine.sendMessage( 2129 MDNS_SERVICE_EVENT, status.result, status.id, status); 2130 } 2131 2132 @Override onGettingServiceAddressStatus(final GetAddressInfo status)2133 public void onGettingServiceAddressStatus(final GetAddressInfo status) { 2134 mStateMachine.sendMessage( 2135 MDNS_SERVICE_EVENT, status.result, status.id, status); 2136 } 2137 2138 @Override getInterfaceVersion()2139 public int getInterfaceVersion() throws RemoteException { 2140 return this.VERSION; 2141 } 2142 2143 @Override getInterfaceHash()2144 public String getInterfaceHash() throws RemoteException { 2145 return this.HASH; 2146 } 2147 } 2148 sendAllOffloadServiceInfos(@onNull OffloadEngineInfo offloadEngineInfo)2149 private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) { 2150 final String targetInterface = offloadEngineInfo.mInterfaceName; 2151 final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine; 2152 final List<MdnsAdvertiser.OffloadServiceInfoWrapper> offloadWrappers = 2153 mAdvertiser.getAllInterfaceOffloadServiceInfos(targetInterface); 2154 for (MdnsAdvertiser.OffloadServiceInfoWrapper wrapper : offloadWrappers) { 2155 try { 2156 offloadEngine.onOffloadServiceUpdated(wrapper.mOffloadServiceInfo); 2157 } catch (RemoteException e) { 2158 // Can happen in regular cases, do not log a stacktrace 2159 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage()); 2160 } 2161 } 2162 } 2163 sendOffloadServiceInfosUpdate(@onNull String targetInterfaceName, @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove)2164 private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName, 2165 @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) { 2166 final int count = mOffloadEngines.beginBroadcast(); 2167 try { 2168 for (int i = 0; i < count; i++) { 2169 final OffloadEngineInfo offloadEngineInfo = 2170 (OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i); 2171 final String interfaceName = offloadEngineInfo.mInterfaceName; 2172 if (!targetInterfaceName.equals(interfaceName) 2173 || ((offloadEngineInfo.mOffloadType 2174 & offloadServiceInfo.getOffloadType()) == 0)) { 2175 continue; 2176 } 2177 try { 2178 if (isRemove) { 2179 mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved( 2180 offloadServiceInfo); 2181 } else { 2182 mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated( 2183 offloadServiceInfo); 2184 } 2185 } catch (RemoteException e) { 2186 // Can happen in regular cases, do not log a stacktrace 2187 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage()); 2188 } 2189 } 2190 } finally { 2191 mOffloadEngines.finishBroadcast(); 2192 } 2193 } 2194 2195 private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback { 2196 // TODO: add a callback to notify when a service is being added on each interface (as soon 2197 // as probing starts), and call mOffloadCallbacks. This callback is for 2198 // OFFLOAD_CAPABILITY_FILTER_REPLIES offload type. 2199 2200 @Override onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo)2201 public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) { 2202 mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId); 2203 final ClientInfo clientInfo = getClientInfoOrLog(transactionId); 2204 if (clientInfo == null) return; 2205 2206 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId); 2207 if (clientRequestId < 0) return; 2208 2209 // onRegisterServiceSucceeded only has the service name and hostname in its info. This 2210 // aligns with historical behavior. 2211 final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null); 2212 cbInfo.setHostname(registeredInfo.getHostname()); 2213 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId); 2214 clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo, request); 2215 } 2216 2217 @Override onRegisterServiceFailed(int transactionId, int errorCode)2218 public void onRegisterServiceFailed(int transactionId, int errorCode) { 2219 final ClientInfo clientInfo = getClientInfoOrLog(transactionId); 2220 if (clientInfo == null) return; 2221 2222 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId); 2223 if (clientRequestId < 0) return; 2224 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId); 2225 clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, false /* isLegacy */, 2226 transactionId, request.calculateRequestDurationMs(mClock.elapsedRealtime())); 2227 } 2228 2229 @Override onOffloadStartOrUpdate(@onNull String interfaceName, @NonNull OffloadServiceInfo offloadServiceInfo)2230 public void onOffloadStartOrUpdate(@NonNull String interfaceName, 2231 @NonNull OffloadServiceInfo offloadServiceInfo) { 2232 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */); 2233 } 2234 2235 @Override onOffloadStop(@onNull String interfaceName, @NonNull OffloadServiceInfo offloadServiceInfo)2236 public void onOffloadStop(@NonNull String interfaceName, 2237 @NonNull OffloadServiceInfo offloadServiceInfo) { 2238 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */); 2239 } 2240 getClientInfoOrLog(int transactionId)2241 private ClientInfo getClientInfoOrLog(int transactionId) { 2242 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId); 2243 if (clientInfo == null) { 2244 Log.e(TAG, String.format("Callback for service %d has no client", transactionId)); 2245 } 2246 return clientInfo; 2247 } 2248 getClientRequestIdOrLog(@onNull ClientInfo info, int transactionId)2249 private int getClientRequestIdOrLog(@NonNull ClientInfo info, int transactionId) { 2250 final int clientRequestId = info.getClientRequestId(transactionId); 2251 if (clientRequestId < 0) { 2252 Log.e(TAG, String.format( 2253 "Client request ID not found for service %d", transactionId)); 2254 } 2255 return clientRequestId; 2256 } 2257 } 2258 2259 private static class ConnectorArgs { 2260 @NonNull public final NsdServiceConnector connector; 2261 @NonNull public final INsdManagerCallback callback; 2262 public final boolean useJavaBackend; 2263 public final int uid; 2264 ConnectorArgs(@onNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback, boolean useJavaBackend, int uid)2265 ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback, 2266 boolean useJavaBackend, int uid) { 2267 this.connector = connector; 2268 this.callback = callback; 2269 this.useJavaBackend = useJavaBackend; 2270 this.uid = uid; 2271 } 2272 } 2273 2274 @Override connect(INsdManagerCallback cb, boolean useJavaBackend)2275 public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) { 2276 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); 2277 final int uid = mDeps.getCallingUid(); 2278 if (cb == null) { 2279 throw new IllegalArgumentException("Unknown client callback from uid=" + uid); 2280 } 2281 if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend); 2282 final INsdServiceConnector connector = new NsdServiceConnector(); 2283 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT, 2284 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, uid))); 2285 return connector; 2286 } 2287 2288 private static class ListenerArgs { 2289 public final NsdServiceConnector connector; 2290 public final NsdServiceInfo serviceInfo; ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo)2291 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) { 2292 this.connector = connector; 2293 this.serviceInfo = serviceInfo; 2294 } 2295 } 2296 2297 private static class AdvertisingArgs { 2298 public final NsdServiceConnector connector; 2299 public final AdvertisingRequest advertisingRequest; 2300 AdvertisingArgs(NsdServiceConnector connector, AdvertisingRequest advertisingRequest)2301 AdvertisingArgs(NsdServiceConnector connector, AdvertisingRequest advertisingRequest) { 2302 this.connector = connector; 2303 this.advertisingRequest = advertisingRequest; 2304 } 2305 } 2306 2307 private static final class DiscoveryArgs { 2308 public final NsdServiceConnector connector; 2309 public final DiscoveryRequest discoveryRequest; DiscoveryArgs(NsdServiceConnector connector, DiscoveryRequest discoveryRequest)2310 DiscoveryArgs(NsdServiceConnector connector, DiscoveryRequest discoveryRequest) { 2311 this.connector = connector; 2312 this.discoveryRequest = discoveryRequest; 2313 } 2314 } 2315 2316 private class NsdServiceConnector extends INsdServiceConnector.Stub 2317 implements IBinder.DeathRecipient { 2318 2319 @Override registerService(int listenerKey, AdvertisingRequest advertisingRequest)2320 public void registerService(int listenerKey, AdvertisingRequest advertisingRequest) 2321 throws RemoteException { 2322 NsdManager.checkServiceInfoForRegistration(advertisingRequest.getServiceInfo()); 2323 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 2324 NsdManager.REGISTER_SERVICE, 0, listenerKey, 2325 new AdvertisingArgs(this, advertisingRequest) 2326 )); 2327 } 2328 2329 @Override unregisterService(int listenerKey)2330 public void unregisterService(int listenerKey) { 2331 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 2332 NsdManager.UNREGISTER_SERVICE, 0, listenerKey, 2333 new ListenerArgs(this, (NsdServiceInfo) null))); 2334 } 2335 2336 @Override discoverServices(int listenerKey, DiscoveryRequest discoveryRequest)2337 public void discoverServices(int listenerKey, DiscoveryRequest discoveryRequest) { 2338 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 2339 NsdManager.DISCOVER_SERVICES, 0, listenerKey, 2340 new DiscoveryArgs(this, discoveryRequest))); 2341 } 2342 2343 @Override stopDiscovery(int listenerKey)2344 public void stopDiscovery(int listenerKey) { 2345 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_DISCOVERY, 2346 0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null))); 2347 } 2348 2349 @Override resolveService(int listenerKey, NsdServiceInfo serviceInfo)2350 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) { 2351 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 2352 NsdManager.RESOLVE_SERVICE, 0, listenerKey, 2353 new ListenerArgs(this, serviceInfo))); 2354 } 2355 2356 @Override stopResolution(int listenerKey)2357 public void stopResolution(int listenerKey) { 2358 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_RESOLUTION, 2359 0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null))); 2360 } 2361 2362 @Override registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo)2363 public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) { 2364 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 2365 NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey, 2366 new ListenerArgs(this, serviceInfo))); 2367 } 2368 2369 @Override unregisterServiceInfoCallback(int listenerKey)2370 public void unregisterServiceInfoCallback(int listenerKey) { 2371 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( 2372 NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey, 2373 new ListenerArgs(this, (NsdServiceInfo) null))); 2374 } 2375 2376 @Override startDaemon()2377 public void startDaemon() { 2378 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.DAEMON_STARTUP, 2379 new ListenerArgs(this, (NsdServiceInfo) null))); 2380 } 2381 2382 @Override binderDied()2383 public void binderDied() { 2384 mNsdStateMachine.sendMessage( 2385 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this)); 2386 2387 } 2388 2389 @Override registerOffloadEngine(String ifaceName, IOffloadEngine cb, @OffloadEngine.OffloadCapability long offloadCapabilities, @OffloadEngine.OffloadType long offloadTypes)2390 public void registerOffloadEngine(String ifaceName, IOffloadEngine cb, 2391 @OffloadEngine.OffloadCapability long offloadCapabilities, 2392 @OffloadEngine.OffloadType long offloadTypes) { 2393 checkOffloadEnginePermission(mContext); 2394 Objects.requireNonNull(ifaceName); 2395 Objects.requireNonNull(cb); 2396 mNsdStateMachine.sendMessage( 2397 mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE, 2398 new OffloadEngineInfo(cb, ifaceName, offloadCapabilities, 2399 offloadTypes))); 2400 } 2401 2402 @Override unregisterOffloadEngine(IOffloadEngine cb)2403 public void unregisterOffloadEngine(IOffloadEngine cb) { 2404 checkOffloadEnginePermission(mContext); 2405 Objects.requireNonNull(cb); 2406 mNsdStateMachine.sendMessage( 2407 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb)); 2408 } 2409 checkOffloadEnginePermission(Context context)2410 private static void checkOffloadEnginePermission(Context context) { 2411 if (!SdkLevel.isAtLeastT()) { 2412 throw new SecurityException("API is not available in before API level 33"); 2413 } 2414 2415 final ArrayList<String> permissionsList = new ArrayList<>(Arrays.asList(NETWORK_STACK, 2416 PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS)); 2417 2418 if (SdkLevel.isAtLeastV()) { 2419 // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V. 2420 permissionsList.add(REGISTER_NSD_OFFLOAD_ENGINE); 2421 } else if (SdkLevel.isAtLeastU()) { 2422 // REGISTER_NSD_OFFLOAD_ENGINE cannot be backport to U. In U, check the DEVICE_POWER 2423 // permission instead. 2424 permissionsList.add(DEVICE_POWER); 2425 } 2426 2427 if (PermissionUtils.hasAnyPermissionOf(context, 2428 permissionsList.toArray(new String[0]))) { 2429 return; 2430 } 2431 throw new SecurityException("Requires one of the following permissions: " 2432 + String.join(", ", permissionsList) + "."); 2433 } 2434 } 2435 sendNsdStateChangeBroadcast(boolean isEnabled)2436 private void sendNsdStateChangeBroadcast(boolean isEnabled) { 2437 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED); 2438 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2439 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED; 2440 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState); 2441 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 2442 } 2443 getUniqueId()2444 private int getUniqueId() { 2445 if (++mUniqueId == INVALID_ID) return ++mUniqueId; 2446 return mUniqueId; 2447 } 2448 registerService(int transactionId, NsdServiceInfo service)2449 private boolean registerService(int transactionId, NsdServiceInfo service) { 2450 if (mMDnsManager == null) { 2451 Log.wtf(TAG, "registerService: mMDnsManager is null"); 2452 return false; 2453 } 2454 2455 if (DBG) { 2456 Log.d(TAG, "registerService: " + transactionId + " " + service); 2457 } 2458 String name = service.getServiceName(); 2459 String type = service.getServiceType(); 2460 int port = service.getPort(); 2461 byte[] textRecord = service.getTxtRecord(); 2462 final int registerInterface = getNetworkInterfaceIndex(service); 2463 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) { 2464 Log.e(TAG, "Interface to register service on not found"); 2465 return false; 2466 } 2467 return mMDnsManager.registerService( 2468 transactionId, name, type, port, textRecord, registerInterface); 2469 } 2470 unregisterService(int transactionId)2471 private boolean unregisterService(int transactionId) { 2472 if (mMDnsManager == null) { 2473 Log.wtf(TAG, "unregisterService: mMDnsManager is null"); 2474 return false; 2475 } 2476 return mMDnsManager.stopOperation(transactionId); 2477 } 2478 discoverServices(int transactionId, DiscoveryRequest discoveryRequest)2479 private boolean discoverServices(int transactionId, DiscoveryRequest discoveryRequest) { 2480 if (mMDnsManager == null) { 2481 Log.wtf(TAG, "discoverServices: mMDnsManager is null"); 2482 return false; 2483 } 2484 2485 final String type = discoveryRequest.getServiceType(); 2486 final int discoverInterface = getNetworkInterfaceIndex(discoveryRequest); 2487 if (discoveryRequest.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) { 2488 Log.e(TAG, "Interface to discover service on not found"); 2489 return false; 2490 } 2491 return mMDnsManager.discover(transactionId, type, discoverInterface); 2492 } 2493 stopServiceDiscovery(int transactionId)2494 private boolean stopServiceDiscovery(int transactionId) { 2495 if (mMDnsManager == null) { 2496 Log.wtf(TAG, "stopServiceDiscovery: mMDnsManager is null"); 2497 return false; 2498 } 2499 return mMDnsManager.stopOperation(transactionId); 2500 } 2501 resolveService(int transactionId, NsdServiceInfo service)2502 private boolean resolveService(int transactionId, NsdServiceInfo service) { 2503 if (mMDnsManager == null) { 2504 Log.wtf(TAG, "resolveService: mMDnsManager is null"); 2505 return false; 2506 } 2507 final String name = service.getServiceName(); 2508 final String type = service.getServiceType(); 2509 final int resolveInterface = getNetworkInterfaceIndex(service); 2510 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) { 2511 Log.e(TAG, "Interface to resolve service on not found"); 2512 return false; 2513 } 2514 return mMDnsManager.resolve(transactionId, name, type, "local.", resolveInterface); 2515 } 2516 2517 /** 2518 * Guess the interface to use to resolve or discover a service on a specific network. 2519 * 2520 * This is an imperfect guess, as for example the network may be gone or not yet fully 2521 * registered. This is fine as failing is correct if the network is gone, and a client 2522 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also 2523 * this is to support the legacy mdnsresponder implementation, which historically resolved 2524 * services on an unspecified network. 2525 */ getNetworkInterfaceIndex(NsdServiceInfo serviceInfo)2526 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) { 2527 final Network network = serviceInfo.getNetwork(); 2528 if (network == null) { 2529 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was 2530 // provided by NsdService from discovery results, and the service was found on an 2531 // interface that has no app-usable Network). 2532 if (serviceInfo.getInterfaceIndex() != 0) { 2533 return serviceInfo.getInterfaceIndex(); 2534 } 2535 return IFACE_IDX_ANY; 2536 } 2537 return getNetworkInterfaceIndex(network); 2538 } 2539 2540 /** 2541 * Returns the interface to use to discover a service on a specific network, or {@link 2542 * IFACE_IDX_ANY} if no network is specified. 2543 */ getNetworkInterfaceIndex(DiscoveryRequest discoveryRequest)2544 private int getNetworkInterfaceIndex(DiscoveryRequest discoveryRequest) { 2545 final Network network = discoveryRequest.getNetwork(); 2546 if (network == null) { 2547 return IFACE_IDX_ANY; 2548 } 2549 return getNetworkInterfaceIndex(network); 2550 } 2551 2552 /** 2553 * Returns the interface of a specific network, or {@link IFACE_IDX_ANY} if no interface is 2554 * associated with {@code network}. 2555 */ getNetworkInterfaceIndex(@onNull Network network)2556 private int getNetworkInterfaceIndex(@NonNull Network network) { 2557 String interfaceName = getNetworkInterfaceName(network); 2558 if (interfaceName == null) { 2559 return IFACE_IDX_ANY; 2560 } 2561 return getNetworkInterfaceIndexByName(interfaceName); 2562 } 2563 getNetworkInterfaceName(@ullable Network network)2564 private String getNetworkInterfaceName(@Nullable Network network) { 2565 if (network == null) { 2566 return null; 2567 } 2568 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 2569 if (cm == null) { 2570 Log.wtf(TAG, "No ConnectivityManager"); 2571 return null; 2572 } 2573 final LinkProperties lp = cm.getLinkProperties(network); 2574 if (lp == null) { 2575 return null; 2576 } 2577 // Only resolve on non-stacked interfaces 2578 return lp.getInterfaceName(); 2579 } 2580 getNetworkInterfaceIndexByName(final String ifaceName)2581 private int getNetworkInterfaceIndexByName(final String ifaceName) { 2582 final NetworkInterface iface; 2583 try { 2584 iface = NetworkInterface.getByName(ifaceName); 2585 } catch (SocketException e) { 2586 Log.e(TAG, "Error querying interface", e); 2587 return IFACE_IDX_ANY; 2588 } 2589 2590 if (iface == null) { 2591 Log.e(TAG, "Interface not found: " + ifaceName); 2592 return IFACE_IDX_ANY; 2593 } 2594 2595 return iface.getIndex(); 2596 } 2597 stopResolveService(int transactionId)2598 private boolean stopResolveService(int transactionId) { 2599 if (mMDnsManager == null) { 2600 Log.wtf(TAG, "stopResolveService: mMDnsManager is null"); 2601 return false; 2602 } 2603 return mMDnsManager.stopOperation(transactionId); 2604 } 2605 getAddrInfo(int transactionId, String hostname, int interfaceIdx)2606 private boolean getAddrInfo(int transactionId, String hostname, int interfaceIdx) { 2607 if (mMDnsManager == null) { 2608 Log.wtf(TAG, "getAddrInfo: mMDnsManager is null"); 2609 return false; 2610 } 2611 return mMDnsManager.getServiceAddress(transactionId, hostname, interfaceIdx); 2612 } 2613 stopGetAddrInfo(int transactionId)2614 private boolean stopGetAddrInfo(int transactionId) { 2615 if (mMDnsManager == null) { 2616 Log.wtf(TAG, "stopGetAddrInfo: mMDnsManager is null"); 2617 return false; 2618 } 2619 return mMDnsManager.stopOperation(transactionId); 2620 } 2621 2622 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)2623 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 2624 if (!PermissionUtils.hasDumpPermission(mContext, TAG, writer)) return; 2625 2626 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 2627 // Dump state machine logs 2628 mNsdStateMachine.dump(fd, pw, args); 2629 2630 // Dump clients 2631 pw.println(); 2632 pw.println("Active clients:"); 2633 pw.increaseIndent(); 2634 HandlerUtils.runWithScissorsForDump(mNsdStateMachine.getHandler(), () -> { 2635 for (ClientInfo clientInfo : mClients.values()) { 2636 pw.println(clientInfo.toString()); 2637 } 2638 }, 10_000); 2639 pw.decreaseIndent(); 2640 2641 // Dump service and clients logs 2642 pw.println(); 2643 pw.println("Logs:"); 2644 pw.increaseIndent(); 2645 mServiceLogs.reverseDump(pw); 2646 pw.decreaseIndent(); 2647 2648 //Dump DiscoveryManager 2649 pw.println(); 2650 pw.println("DiscoveryManager:"); 2651 pw.increaseIndent(); 2652 HandlerUtils.runWithScissorsForDump( 2653 mNsdStateMachine.getHandler(), () -> mMdnsDiscoveryManager.dump(pw), 10_000); 2654 pw.decreaseIndent(); 2655 } 2656 2657 private abstract static class ClientRequest { 2658 private final int mTransactionId; 2659 private final long mStartTimeMs; 2660 private int mFoundServiceCount = 0; 2661 private int mLostServiceCount = 0; 2662 private final Set<String> mServices = new ArraySet<>(); 2663 private boolean mIsServiceFromCache = false; 2664 private int mSentQueryCount = NO_SENT_QUERY_COUNT; 2665 ClientRequest(int transactionId, long startTimeMs)2666 private ClientRequest(int transactionId, long startTimeMs) { 2667 mTransactionId = transactionId; 2668 mStartTimeMs = startTimeMs; 2669 } 2670 calculateRequestDurationMs(long stopTimeMs)2671 public long calculateRequestDurationMs(long stopTimeMs) { 2672 return stopTimeMs - mStartTimeMs; 2673 } 2674 onServiceFound(String serviceName)2675 public void onServiceFound(String serviceName) { 2676 mFoundServiceCount++; 2677 if (mServices.size() <= MAX_SERVICES_COUNT_METRIC_PER_CLIENT) { 2678 mServices.add(serviceName); 2679 } 2680 } 2681 onServiceLost()2682 public void onServiceLost() { 2683 mLostServiceCount++; 2684 } 2685 getFoundServiceCount()2686 public int getFoundServiceCount() { 2687 return mFoundServiceCount; 2688 } 2689 getLostServiceCount()2690 public int getLostServiceCount() { 2691 return mLostServiceCount; 2692 } 2693 getServicesCount()2694 public int getServicesCount() { 2695 return mServices.size(); 2696 } 2697 setServiceFromCache(boolean isServiceFromCache)2698 public void setServiceFromCache(boolean isServiceFromCache) { 2699 mIsServiceFromCache = isServiceFromCache; 2700 } 2701 isServiceFromCache()2702 public boolean isServiceFromCache() { 2703 return mIsServiceFromCache; 2704 } 2705 onQuerySent()2706 public void onQuerySent() { 2707 mSentQueryCount++; 2708 } 2709 getSentQueryCount()2710 public int getSentQueryCount() { 2711 return mSentQueryCount; 2712 } 2713 2714 @NonNull 2715 @Override toString()2716 public String toString() { 2717 return getRequestDescriptor() + " {" + mTransactionId 2718 + ", startTime " + mStartTimeMs 2719 + ", foundServices " + mFoundServiceCount 2720 + ", lostServices " + mLostServiceCount 2721 + ", fromCache " + mIsServiceFromCache 2722 + ", sentQueries " + mSentQueryCount 2723 + "}"; 2724 } 2725 2726 @NonNull getRequestDescriptor()2727 protected abstract String getRequestDescriptor(); 2728 } 2729 2730 private static class LegacyClientRequest extends ClientRequest { 2731 private final int mRequestCode; 2732 LegacyClientRequest(int transactionId, int requestCode, long startTimeMs)2733 private LegacyClientRequest(int transactionId, int requestCode, long startTimeMs) { 2734 super(transactionId, startTimeMs); 2735 mRequestCode = requestCode; 2736 } 2737 2738 @NonNull 2739 @Override getRequestDescriptor()2740 protected String getRequestDescriptor() { 2741 return "Legacy (" + mRequestCode + ")"; 2742 } 2743 } 2744 2745 private abstract static class JavaBackendClientRequest extends ClientRequest { 2746 @Nullable 2747 private final Network mRequestedNetwork; 2748 JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork, long startTimeMs)2749 private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork, 2750 long startTimeMs) { 2751 super(transactionId, startTimeMs); 2752 mRequestedNetwork = requestedNetwork; 2753 } 2754 2755 @Nullable getRequestedNetwork()2756 public Network getRequestedNetwork() { 2757 return mRequestedNetwork; 2758 } 2759 } 2760 2761 private static class AdvertiserClientRequest extends JavaBackendClientRequest { 2762 @NonNull 2763 private final String mServiceFullName; 2764 AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork, @NonNull String serviceFullName, long startTimeMs)2765 private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork, 2766 @NonNull String serviceFullName, long startTimeMs) { 2767 super(transactionId, requestedNetwork, startTimeMs); 2768 mServiceFullName = serviceFullName; 2769 } 2770 2771 @NonNull 2772 @Override getRequestDescriptor()2773 public String getRequestDescriptor() { 2774 return String.format("Advertiser: serviceFullName=%s, net=%s", 2775 mServiceFullName, getRequestedNetwork()); 2776 } 2777 } 2778 2779 private static class DiscoveryManagerRequest extends JavaBackendClientRequest { 2780 @NonNull 2781 private final MdnsListener mListener; 2782 DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener, @Nullable Network requestedNetwork, long startTimeMs)2783 private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener, 2784 @Nullable Network requestedNetwork, long startTimeMs) { 2785 super(transactionId, requestedNetwork, startTimeMs); 2786 mListener = listener; 2787 } 2788 2789 @NonNull 2790 @Override getRequestDescriptor()2791 public String getRequestDescriptor() { 2792 return String.format("Discovery/%s, net=%s", mListener, getRequestedNetwork()); 2793 } 2794 } 2795 2796 /* Information tracked per client */ 2797 private class ClientInfo { 2798 2799 /** 2800 * Maximum number of requests (callbacks) for a client. 2801 * 2802 * 200 listeners should be more than enough for most use-cases: even if a client tries to 2803 * file callbacks for every service on a local network, there are generally much less than 2804 * 200 devices on a local network (a /24 only allows 255 IPv4 devices), and while some 2805 * devices may have multiple services, many devices do not advertise any. 2806 */ 2807 private static final int MAX_LIMIT = 200; 2808 private final INsdManagerCallback mCb; 2809 /* Remembers a resolved service until getaddrinfo completes */ 2810 private NsdServiceInfo mResolvedService; 2811 2812 /* A map from client request ID (listenerKey) to the request */ 2813 private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>(); 2814 2815 // The target SDK of this client < Build.VERSION_CODES.S 2816 private boolean mIsPreSClient = false; 2817 private final int mUid; 2818 // The flag of using java backend if the client's target SDK >= U 2819 private final boolean mUseJavaBackend; 2820 // Store client logs 2821 private final SharedLog mClientLogs; 2822 // Report the nsd metrics data 2823 private final NetworkNsdReportedMetrics mMetrics; 2824 ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend, SharedLog sharedLog, NetworkNsdReportedMetrics metrics)2825 private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend, 2826 SharedLog sharedLog, NetworkNsdReportedMetrics metrics) { 2827 mCb = cb; 2828 mUid = uid; 2829 mUseJavaBackend = useJavaBackend; 2830 mClientLogs = sharedLog; 2831 mClientLogs.log("New client. useJavaBackend=" + useJavaBackend); 2832 mMetrics = metrics; 2833 } 2834 2835 @Override toString()2836 public String toString() { 2837 StringBuilder sb = new StringBuilder(); 2838 sb.append("mUid ").append(mUid).append(", "); 2839 sb.append("mResolvedService ").append(mResolvedService).append(", "); 2840 sb.append("mIsLegacy ").append(mIsPreSClient).append(", "); 2841 sb.append("mUseJavaBackend ").append(mUseJavaBackend).append(", "); 2842 sb.append("mClientRequests:\n"); 2843 for (int i = 0; i < mClientRequests.size(); i++) { 2844 int clientRequestId = mClientRequests.keyAt(i); 2845 sb.append(" ").append(clientRequestId) 2846 .append(": ").append(mClientRequests.valueAt(i).toString()) 2847 .append("\n"); 2848 } 2849 return sb.toString(); 2850 } 2851 getUid()2852 public int getUid() { 2853 return mUid; 2854 } 2855 isPreSClient()2856 private boolean isPreSClient() { 2857 return mIsPreSClient; 2858 } 2859 setPreSClient()2860 private void setPreSClient() { 2861 mIsPreSClient = true; 2862 } 2863 unregisterMdnsListenerFromRequest(ClientRequest request)2864 private MdnsListener unregisterMdnsListenerFromRequest(ClientRequest request) { 2865 final MdnsListener listener = 2866 ((DiscoveryManagerRequest) request).mListener; 2867 mMdnsDiscoveryManager.unregisterListener( 2868 listener.getListenedServiceType(), listener); 2869 return listener; 2870 } 2871 2872 // Remove any pending requests from the global map when we get rid of a client, 2873 // and send cancellations to the daemon. expungeAllRequests()2874 private void expungeAllRequests() { 2875 mClientLogs.log("Client unregistered. expungeAllRequests!"); 2876 // TODO: to keep handler responsive, do not clean all requests for that client at once. 2877 for (int i = 0; i < mClientRequests.size(); i++) { 2878 final int clientRequestId = mClientRequests.keyAt(i); 2879 final ClientRequest request = mClientRequests.valueAt(i); 2880 final int transactionId = request.mTransactionId; 2881 mTransactionIdToClientInfoMap.remove(transactionId); 2882 if (DBG) { 2883 Log.d(TAG, "Terminating clientRequestId " + clientRequestId 2884 + " transactionId " + transactionId 2885 + " type " + mClientRequests.get(clientRequestId)); 2886 } 2887 2888 if (request instanceof DiscoveryManagerRequest) { 2889 final MdnsListener listener = unregisterMdnsListenerFromRequest(request); 2890 if (listener instanceof DiscoveryListener) { 2891 mMetrics.reportServiceDiscoveryStop(false /* isLegacy */, transactionId, 2892 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 2893 request.getFoundServiceCount(), 2894 request.getLostServiceCount(), 2895 request.getServicesCount(), 2896 request.getSentQueryCount(), 2897 request.isServiceFromCache()); 2898 } else if (listener instanceof ResolutionListener) { 2899 mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId, 2900 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 2901 request.getSentQueryCount()); 2902 } else if (listener instanceof ServiceInfoListener) { 2903 mMetrics.reportServiceInfoCallbackUnregistered(transactionId, 2904 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 2905 request.getFoundServiceCount(), 2906 request.getLostServiceCount(), 2907 request.isServiceFromCache(), 2908 request.getSentQueryCount()); 2909 } 2910 continue; 2911 } 2912 2913 if (request instanceof AdvertiserClientRequest) { 2914 final AdvertiserMetrics metrics = 2915 mAdvertiser.getAdvertiserMetrics(transactionId); 2916 mAdvertiser.removeService(transactionId); 2917 mMetrics.reportServiceUnregistration(false /* isLegacy */, transactionId, 2918 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 2919 metrics.mRepliedRequestsCount, metrics.mSentPacketCount, 2920 metrics.mConflictDuringProbingCount, 2921 metrics.mConflictAfterProbingCount); 2922 continue; 2923 } 2924 2925 if (!(request instanceof LegacyClientRequest)) { 2926 throw new IllegalStateException("Unknown request type: " + request.getClass()); 2927 } 2928 2929 switch (((LegacyClientRequest) request).mRequestCode) { 2930 case NsdManager.DISCOVER_SERVICES: 2931 stopServiceDiscovery(transactionId); 2932 mMetrics.reportServiceDiscoveryStop(true /* isLegacy */, transactionId, 2933 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 2934 request.getFoundServiceCount(), 2935 request.getLostServiceCount(), 2936 request.getServicesCount(), 2937 NO_SENT_QUERY_COUNT, 2938 request.isServiceFromCache()); 2939 break; 2940 case NsdManager.RESOLVE_SERVICE: 2941 stopResolveService(transactionId); 2942 mMetrics.reportServiceResolutionStop(true /* isLegacy */, transactionId, 2943 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 2944 NO_SENT_QUERY_COUNT); 2945 break; 2946 case NsdManager.REGISTER_SERVICE: 2947 unregisterService(transactionId); 2948 mMetrics.reportServiceUnregistration(true /* isLegacy */, transactionId, 2949 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 2950 NO_PACKET /* repliedRequestsCount */, 2951 NO_PACKET /* sentPacketCount */, 2952 0 /* conflictDuringProbingCount */, 2953 0 /* conflictAfterProbingCount */); 2954 break; 2955 default: 2956 break; 2957 } 2958 } 2959 mClientRequests.clear(); 2960 updateMulticastLock(); 2961 } 2962 2963 /** 2964 * Returns true if this client has any Java backend request that requests one of the given 2965 * networks. 2966 */ hasAnyJavaBackendRequestForNetworks(@onNull ArraySet<Network> networks)2967 boolean hasAnyJavaBackendRequestForNetworks(@NonNull ArraySet<Network> networks) { 2968 for (int i = 0; i < mClientRequests.size(); i++) { 2969 final ClientRequest req = mClientRequests.valueAt(i); 2970 if (!(req instanceof JavaBackendClientRequest)) { 2971 continue; 2972 } 2973 final Network reqNetwork = ((JavaBackendClientRequest) mClientRequests.valueAt(i)) 2974 .getRequestedNetwork(); 2975 if (MdnsUtils.isAnyNetworkMatched(reqNetwork, networks)) { 2976 return true; 2977 } 2978 } 2979 return false; 2980 } 2981 2982 // mClientRequests is a sparse array of client request id -> ClientRequest. For a given 2983 // transaction id, return the corresponding client request id. getClientRequestId(final int transactionId)2984 private int getClientRequestId(final int transactionId) { 2985 for (int i = 0; i < mClientRequests.size(); i++) { 2986 if (mClientRequests.valueAt(i).mTransactionId == transactionId) { 2987 return mClientRequests.keyAt(i); 2988 } 2989 } 2990 return -1; 2991 } 2992 log(String message)2993 private void log(String message) { 2994 mClientLogs.log(message); 2995 } 2996 isLegacyClientRequest(@onNull ClientRequest request)2997 private static boolean isLegacyClientRequest(@NonNull ClientRequest request) { 2998 return !(request instanceof DiscoveryManagerRequest) 2999 && !(request instanceof AdvertiserClientRequest); 3000 } 3001 onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest, ClientRequest request)3002 void onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest, 3003 ClientRequest request) { 3004 mMetrics.reportServiceDiscoveryStarted( 3005 isLegacyClientRequest(request), request.mTransactionId); 3006 try { 3007 mCb.onDiscoverServicesStarted(listenerKey, discoveryRequest); 3008 } catch (RemoteException e) { 3009 Log.e(TAG, "Error calling onDiscoverServicesStarted", e); 3010 } 3011 } onDiscoverServicesFailedImmediately(int listenerKey, int error, boolean isLegacy)3012 void onDiscoverServicesFailedImmediately(int listenerKey, int error, boolean isLegacy) { 3013 onDiscoverServicesFailed(listenerKey, error, isLegacy, NO_TRANSACTION, 3014 0L /* durationMs */); 3015 } 3016 onDiscoverServicesFailed(int listenerKey, int error, boolean isLegacy, int transactionId, long durationMs)3017 void onDiscoverServicesFailed(int listenerKey, int error, boolean isLegacy, 3018 int transactionId, long durationMs) { 3019 mMetrics.reportServiceDiscoveryFailed(isLegacy, transactionId, durationMs); 3020 try { 3021 mCb.onDiscoverServicesFailed(listenerKey, error); 3022 } catch (RemoteException e) { 3023 Log.e(TAG, "Error calling onDiscoverServicesFailed", e); 3024 } 3025 } 3026 onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request)3027 void onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request) { 3028 request.onServiceFound(info.getServiceName()); 3029 try { 3030 mCb.onServiceFound(listenerKey, info); 3031 } catch (RemoteException e) { 3032 Log.e(TAG, "Error calling onServiceFound(", e); 3033 } 3034 } 3035 onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request)3036 void onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request) { 3037 request.onServiceLost(); 3038 try { 3039 mCb.onServiceLost(listenerKey, info); 3040 } catch (RemoteException e) { 3041 Log.e(TAG, "Error calling onServiceLost(", e); 3042 } 3043 } 3044 onStopDiscoveryFailed(int listenerKey, int error)3045 void onStopDiscoveryFailed(int listenerKey, int error) { 3046 try { 3047 mCb.onStopDiscoveryFailed(listenerKey, error); 3048 } catch (RemoteException e) { 3049 Log.e(TAG, "Error calling onStopDiscoveryFailed", e); 3050 } 3051 } 3052 onStopDiscoverySucceeded(int listenerKey, ClientRequest request)3053 void onStopDiscoverySucceeded(int listenerKey, ClientRequest request) { 3054 mMetrics.reportServiceDiscoveryStop( 3055 isLegacyClientRequest(request), 3056 request.mTransactionId, 3057 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 3058 request.getFoundServiceCount(), 3059 request.getLostServiceCount(), 3060 request.getServicesCount(), 3061 request.getSentQueryCount(), 3062 request.isServiceFromCache()); 3063 try { 3064 mCb.onStopDiscoverySucceeded(listenerKey); 3065 } catch (RemoteException e) { 3066 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e); 3067 } 3068 } 3069 onRegisterServiceFailedImmediately(int listenerKey, int error, boolean isLegacy)3070 void onRegisterServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) { 3071 onRegisterServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION, 3072 0L /* durationMs */); 3073 } 3074 onRegisterServiceFailed(int listenerKey, int error, boolean isLegacy, int transactionId, long durationMs)3075 void onRegisterServiceFailed(int listenerKey, int error, boolean isLegacy, 3076 int transactionId, long durationMs) { 3077 mMetrics.reportServiceRegistrationFailed(isLegacy, transactionId, durationMs); 3078 try { 3079 mCb.onRegisterServiceFailed(listenerKey, error); 3080 } catch (RemoteException e) { 3081 Log.e(TAG, "Error calling onRegisterServiceFailed", e); 3082 } 3083 } 3084 onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info, ClientRequest request)3085 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info, 3086 ClientRequest request) { 3087 mMetrics.reportServiceRegistrationSucceeded(isLegacyClientRequest(request), 3088 request.mTransactionId, 3089 request.calculateRequestDurationMs(mClock.elapsedRealtime())); 3090 try { 3091 mCb.onRegisterServiceSucceeded(listenerKey, info); 3092 } catch (RemoteException e) { 3093 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e); 3094 } 3095 } 3096 onUnregisterServiceFailed(int listenerKey, int error)3097 void onUnregisterServiceFailed(int listenerKey, int error) { 3098 try { 3099 mCb.onUnregisterServiceFailed(listenerKey, error); 3100 } catch (RemoteException e) { 3101 Log.e(TAG, "Error calling onUnregisterServiceFailed", e); 3102 } 3103 } 3104 onUnregisterServiceSucceeded(int listenerKey, ClientRequest request, AdvertiserMetrics metrics)3105 void onUnregisterServiceSucceeded(int listenerKey, ClientRequest request, 3106 AdvertiserMetrics metrics) { 3107 mMetrics.reportServiceUnregistration(isLegacyClientRequest(request), 3108 request.mTransactionId, 3109 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 3110 metrics.mRepliedRequestsCount, metrics.mSentPacketCount, 3111 metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount); 3112 try { 3113 mCb.onUnregisterServiceSucceeded(listenerKey); 3114 } catch (RemoteException e) { 3115 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e); 3116 } 3117 } 3118 onResolveServiceFailedImmediately(int listenerKey, int error, boolean isLegacy)3119 void onResolveServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) { 3120 onResolveServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION, 3121 0L /* durationMs */); 3122 } 3123 onResolveServiceFailed(int listenerKey, int error, boolean isLegacy, int transactionId, long durationMs)3124 void onResolveServiceFailed(int listenerKey, int error, boolean isLegacy, 3125 int transactionId, long durationMs) { 3126 mMetrics.reportServiceResolutionFailed(isLegacy, transactionId, durationMs); 3127 try { 3128 mCb.onResolveServiceFailed(listenerKey, error); 3129 } catch (RemoteException e) { 3130 Log.e(TAG, "Error calling onResolveServiceFailed", e); 3131 } 3132 } 3133 onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info, ClientRequest request)3134 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info, 3135 ClientRequest request) { 3136 mMetrics.reportServiceResolved( 3137 isLegacyClientRequest(request), 3138 request.mTransactionId, 3139 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 3140 request.isServiceFromCache(), 3141 request.getSentQueryCount()); 3142 try { 3143 mCb.onResolveServiceSucceeded(listenerKey, info); 3144 } catch (RemoteException e) { 3145 Log.e(TAG, "Error calling onResolveServiceSucceeded", e); 3146 } 3147 } 3148 onStopResolutionFailed(int listenerKey, int error)3149 void onStopResolutionFailed(int listenerKey, int error) { 3150 try { 3151 mCb.onStopResolutionFailed(listenerKey, error); 3152 } catch (RemoteException e) { 3153 Log.e(TAG, "Error calling onStopResolutionFailed", e); 3154 } 3155 } 3156 onStopResolutionSucceeded(int listenerKey, ClientRequest request)3157 void onStopResolutionSucceeded(int listenerKey, ClientRequest request) { 3158 mMetrics.reportServiceResolutionStop( 3159 isLegacyClientRequest(request), 3160 request.mTransactionId, 3161 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 3162 request.getSentQueryCount()); 3163 try { 3164 mCb.onStopResolutionSucceeded(listenerKey); 3165 } catch (RemoteException e) { 3166 Log.e(TAG, "Error calling onStopResolutionSucceeded", e); 3167 } 3168 } 3169 onServiceInfoCallbackRegistrationFailed(int listenerKey, int error)3170 void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) { 3171 mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION); 3172 try { 3173 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error); 3174 } catch (RemoteException e) { 3175 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e); 3176 } 3177 } 3178 onServiceInfoCallbackRegistered(int transactionId)3179 void onServiceInfoCallbackRegistered(int transactionId) { 3180 mMetrics.reportServiceInfoCallbackRegistered(transactionId); 3181 } 3182 onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request)3183 void onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request) { 3184 request.onServiceFound(info.getServiceName()); 3185 try { 3186 mCb.onServiceUpdated(listenerKey, info); 3187 } catch (RemoteException e) { 3188 Log.e(TAG, "Error calling onServiceUpdated", e); 3189 } 3190 } 3191 onServiceUpdatedLost(int listenerKey, ClientRequest request)3192 void onServiceUpdatedLost(int listenerKey, ClientRequest request) { 3193 request.onServiceLost(); 3194 try { 3195 mCb.onServiceUpdatedLost(listenerKey); 3196 } catch (RemoteException e) { 3197 Log.e(TAG, "Error calling onServiceUpdatedLost", e); 3198 } 3199 } 3200 onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request)3201 void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) { 3202 mMetrics.reportServiceInfoCallbackUnregistered( 3203 request.mTransactionId, 3204 request.calculateRequestDurationMs(mClock.elapsedRealtime()), 3205 request.getFoundServiceCount(), 3206 request.getLostServiceCount(), 3207 request.isServiceFromCache(), 3208 request.getSentQueryCount()); 3209 try { 3210 mCb.onServiceInfoCallbackUnregistered(listenerKey); 3211 } catch (RemoteException e) { 3212 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e); 3213 } 3214 } 3215 } 3216 } 3217