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 android.net.nsd; 18 19 import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND; 20 import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER; 21 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SdkConstant; 27 import android.annotation.SdkConstant.SdkConstantType; 28 import android.annotation.SystemService; 29 import android.app.compat.CompatChanges; 30 import android.content.Context; 31 import android.net.ConnectivityManager; 32 import android.net.ConnectivityManager.NetworkCallback; 33 import android.net.Network; 34 import android.net.NetworkRequest; 35 import android.os.Handler; 36 import android.os.HandlerThread; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.RemoteException; 40 import android.text.TextUtils; 41 import android.util.ArrayMap; 42 import android.util.ArraySet; 43 import android.util.Log; 44 import android.util.SparseArray; 45 46 import com.android.internal.annotations.GuardedBy; 47 import com.android.internal.annotations.VisibleForTesting; 48 49 import java.lang.annotation.Retention; 50 import java.lang.annotation.RetentionPolicy; 51 import java.util.Objects; 52 import java.util.concurrent.Executor; 53 54 /** 55 * The Network Service Discovery Manager class provides the API to discover services 56 * on a network. As an example, if device A and device B are connected over a Wi-Fi 57 * network, a game registered on device A can be discovered by a game on device 58 * B. Another example use case is an application discovering printers on the network. 59 * 60 * <p> The API currently supports DNS based service discovery and discovery is currently 61 * limited to a local network over Multicast DNS. DNS service discovery is described at 62 * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt 63 * 64 * <p> The API is asynchronous, and responses to requests from an application are on listener 65 * callbacks on a separate internal thread. 66 * 67 * <p> There are three main operations the API supports - registration, discovery and resolution. 68 * <pre> 69 * Application start 70 * | 71 * | 72 * | onServiceRegistered() 73 * Register any local services / 74 * to be advertised with \ 75 * registerService() onRegistrationFailed() 76 * | 77 * | 78 * discoverServices() 79 * | 80 * Maintain a list to track 81 * discovered services 82 * | 83 * |---------> 84 * | | 85 * | onServiceFound() 86 * | | 87 * | add service to list 88 * | | 89 * |<---------- 90 * | 91 * |---------> 92 * | | 93 * | onServiceLost() 94 * | | 95 * | remove service from list 96 * | | 97 * |<---------- 98 * | 99 * | 100 * | Connect to a service 101 * | from list ? 102 * | 103 * resolveService() 104 * | 105 * onServiceResolved() 106 * | 107 * Establish connection to service 108 * with the host and port information 109 * 110 * </pre> 111 * An application that needs to advertise itself over a network for other applications to 112 * discover it can do so with a call to {@link #registerService}. If Example is a http based 113 * application that can provide HTML data to peer services, it can register a name "Example" 114 * with service type "_http._tcp". A successful registration is notified with a callback to 115 * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified 116 * over {@link RegistrationListener#onRegistrationFailed} 117 * 118 * <p> A peer application looking for http services can initiate a discovery for "_http._tcp" 119 * with a call to {@link #discoverServices}. A service found is notified with a callback 120 * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on 121 * {@link DiscoveryListener#onServiceLost}. 122 * 123 * <p> Once the peer application discovers the "Example" http service, and either needs to read the 124 * attributes of the service or wants to receive data from the "Example" application, it can 125 * initiate a resolve with {@link #resolveService} to resolve the attributes, host, and port 126 * details. A successful resolve is notified on {@link ResolveListener#onServiceResolved} and a 127 * failure is notified on {@link ResolveListener#onResolveFailed}. 128 * 129 * Applications can reserve for a service type at 130 * http://www.iana.org/form/ports-service. Existing services can be found at 131 * http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml 132 * 133 * @see NsdServiceInfo 134 */ 135 @SystemService(Context.NSD_SERVICE) 136 public final class NsdManager { 137 private static final String TAG = NsdManager.class.getSimpleName(); 138 private static final boolean DBG = false; 139 140 /** 141 * Broadcast intent action to indicate whether network service discovery is 142 * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state 143 * information as int. 144 * 145 * @see #EXTRA_NSD_STATE 146 */ 147 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 148 public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED"; 149 150 /** 151 * The lookup key for an int that indicates whether network service discovery is enabled 152 * or disabled. Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. 153 * 154 * @see #NSD_STATE_DISABLED 155 * @see #NSD_STATE_ENABLED 156 */ 157 public static final String EXTRA_NSD_STATE = "nsd_state"; 158 159 /** 160 * Network service discovery is disabled 161 * 162 * @see #ACTION_NSD_STATE_CHANGED 163 */ 164 // TODO: Deprecate this since NSD service is never disabled. 165 public static final int NSD_STATE_DISABLED = 1; 166 167 /** 168 * Network service discovery is enabled 169 * 170 * @see #ACTION_NSD_STATE_CHANGED 171 */ 172 public static final int NSD_STATE_ENABLED = 2; 173 174 /** @hide */ 175 public static final int DISCOVER_SERVICES = 1; 176 /** @hide */ 177 public static final int DISCOVER_SERVICES_STARTED = 2; 178 /** @hide */ 179 public static final int DISCOVER_SERVICES_FAILED = 3; 180 /** @hide */ 181 public static final int SERVICE_FOUND = 4; 182 /** @hide */ 183 public static final int SERVICE_LOST = 5; 184 185 /** @hide */ 186 public static final int STOP_DISCOVERY = 6; 187 /** @hide */ 188 public static final int STOP_DISCOVERY_FAILED = 7; 189 /** @hide */ 190 public static final int STOP_DISCOVERY_SUCCEEDED = 8; 191 192 /** @hide */ 193 public static final int REGISTER_SERVICE = 9; 194 /** @hide */ 195 public static final int REGISTER_SERVICE_FAILED = 10; 196 /** @hide */ 197 public static final int REGISTER_SERVICE_SUCCEEDED = 11; 198 199 /** @hide */ 200 public static final int UNREGISTER_SERVICE = 12; 201 /** @hide */ 202 public static final int UNREGISTER_SERVICE_FAILED = 13; 203 /** @hide */ 204 public static final int UNREGISTER_SERVICE_SUCCEEDED = 14; 205 206 /** @hide */ 207 public static final int RESOLVE_SERVICE = 15; 208 /** @hide */ 209 public static final int RESOLVE_SERVICE_FAILED = 16; 210 /** @hide */ 211 public static final int RESOLVE_SERVICE_SUCCEEDED = 17; 212 213 /** @hide */ 214 public static final int DAEMON_CLEANUP = 18; 215 /** @hide */ 216 public static final int DAEMON_STARTUP = 19; 217 218 /** @hide */ 219 public static final int MDNS_SERVICE_EVENT = 20; 220 221 /** @hide */ 222 public static final int REGISTER_CLIENT = 21; 223 /** @hide */ 224 public static final int UNREGISTER_CLIENT = 22; 225 226 /** @hide */ 227 public static final int MDNS_DISCOVERY_MANAGER_EVENT = 23; 228 229 /** @hide */ 230 public static final int STOP_RESOLUTION = 24; 231 /** @hide */ 232 public static final int STOP_RESOLUTION_FAILED = 25; 233 /** @hide */ 234 public static final int STOP_RESOLUTION_SUCCEEDED = 26; 235 236 /** @hide */ 237 public static final int REGISTER_SERVICE_CALLBACK = 27; 238 /** @hide */ 239 public static final int REGISTER_SERVICE_CALLBACK_FAILED = 28; 240 /** @hide */ 241 public static final int SERVICE_UPDATED = 29; 242 /** @hide */ 243 public static final int SERVICE_UPDATED_LOST = 30; 244 245 /** @hide */ 246 public static final int UNREGISTER_SERVICE_CALLBACK = 31; 247 /** @hide */ 248 public static final int UNREGISTER_SERVICE_CALLBACK_SUCCEEDED = 32; 249 250 /** Dns based service discovery protocol */ 251 public static final int PROTOCOL_DNS_SD = 0x0001; 252 253 private static final SparseArray<String> EVENT_NAMES = new SparseArray<>(); 254 static { EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES")255 EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES"); EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED")256 EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED"); EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED")257 EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED"); EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND")258 EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND"); EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST")259 EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST"); EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY")260 EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY"); EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED")261 EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED"); EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED")262 EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED"); EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE")263 EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE"); EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED")264 EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED"); EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED")265 EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED"); EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE")266 EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE"); EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED")267 EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED"); EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED")268 EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED"); EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE")269 EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE"); EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED")270 EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED"); EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED")271 EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED"); EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP")272 EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP"); EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP")273 EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP"); EVENT_NAMES.put(MDNS_SERVICE_EVENT, "MDNS_SERVICE_EVENT")274 EVENT_NAMES.put(MDNS_SERVICE_EVENT, "MDNS_SERVICE_EVENT"); EVENT_NAMES.put(STOP_RESOLUTION, "STOP_RESOLUTION")275 EVENT_NAMES.put(STOP_RESOLUTION, "STOP_RESOLUTION"); EVENT_NAMES.put(STOP_RESOLUTION_FAILED, "STOP_RESOLUTION_FAILED")276 EVENT_NAMES.put(STOP_RESOLUTION_FAILED, "STOP_RESOLUTION_FAILED"); EVENT_NAMES.put(STOP_RESOLUTION_SUCCEEDED, "STOP_RESOLUTION_SUCCEEDED")277 EVENT_NAMES.put(STOP_RESOLUTION_SUCCEEDED, "STOP_RESOLUTION_SUCCEEDED"); EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK, "REGISTER_SERVICE_CALLBACK")278 EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK, "REGISTER_SERVICE_CALLBACK"); EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK_FAILED, "REGISTER_SERVICE_CALLBACK_FAILED")279 EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK_FAILED, "REGISTER_SERVICE_CALLBACK_FAILED"); EVENT_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED")280 EVENT_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED"); EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK, "UNREGISTER_SERVICE_CALLBACK")281 EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK, "UNREGISTER_SERVICE_CALLBACK"); EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, "UNREGISTER_SERVICE_CALLBACK_SUCCEEDED")282 EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, 283 "UNREGISTER_SERVICE_CALLBACK_SUCCEEDED"); EVENT_NAMES.put(MDNS_DISCOVERY_MANAGER_EVENT, "MDNS_DISCOVERY_MANAGER_EVENT")284 EVENT_NAMES.put(MDNS_DISCOVERY_MANAGER_EVENT, "MDNS_DISCOVERY_MANAGER_EVENT"); EVENT_NAMES.put(REGISTER_CLIENT, "REGISTER_CLIENT")285 EVENT_NAMES.put(REGISTER_CLIENT, "REGISTER_CLIENT"); EVENT_NAMES.put(UNREGISTER_CLIENT, "UNREGISTER_CLIENT")286 EVENT_NAMES.put(UNREGISTER_CLIENT, "UNREGISTER_CLIENT"); 287 } 288 289 /** @hide */ nameOf(int event)290 public static String nameOf(int event) { 291 String name = EVENT_NAMES.get(event); 292 if (name == null) { 293 return Integer.toString(event); 294 } 295 return name; 296 } 297 298 private static final int FIRST_LISTENER_KEY = 1; 299 300 private final INsdServiceConnector mService; 301 private final Context mContext; 302 303 private int mListenerKey = FIRST_LISTENER_KEY; 304 @GuardedBy("mMapLock") 305 private final SparseArray mListenerMap = new SparseArray(); 306 @GuardedBy("mMapLock") 307 private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); 308 @GuardedBy("mMapLock") 309 private final SparseArray<Executor> mExecutorMap = new SparseArray<>(); 310 private final Object mMapLock = new Object(); 311 // Map of listener key sent by client -> per-network discovery tracker 312 @GuardedBy("mPerNetworkDiscoveryMap") 313 private final ArrayMap<Integer, PerNetworkDiscoveryTracker> 314 mPerNetworkDiscoveryMap = new ArrayMap<>(); 315 316 private final ServiceHandler mHandler; 317 318 private class PerNetworkDiscoveryTracker { 319 final String mServiceType; 320 final int mProtocolType; 321 final DiscoveryListener mBaseListener; 322 final Executor mBaseExecutor; 323 final ArrayMap<Network, DelegatingDiscoveryListener> mPerNetworkListeners = 324 new ArrayMap<>(); 325 326 final NetworkCallback mNetworkCb = new NetworkCallback() { 327 @Override 328 public void onAvailable(@NonNull Network network) { 329 final DelegatingDiscoveryListener wrappedListener = new DelegatingDiscoveryListener( 330 network, mBaseListener, mBaseExecutor); 331 mPerNetworkListeners.put(network, wrappedListener); 332 // Run discovery callbacks inline on the service handler thread, which is the 333 // same thread used by this NetworkCallback, but DelegatingDiscoveryListener will 334 // use the base executor to run the wrapped callbacks. 335 discoverServices(mServiceType, mProtocolType, network, Runnable::run, 336 wrappedListener); 337 } 338 339 @Override 340 public void onLost(@NonNull Network network) { 341 final DelegatingDiscoveryListener listener = mPerNetworkListeners.get(network); 342 if (listener == null) return; 343 listener.notifyAllServicesLost(); 344 // Listener will be removed from map in discovery stopped callback 345 stopServiceDiscovery(listener); 346 } 347 }; 348 349 // Accessed from mHandler 350 private boolean mStopRequested; 351 start(@onNull NetworkRequest request)352 public void start(@NonNull NetworkRequest request) { 353 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 354 cm.registerNetworkCallback(request, mNetworkCb, mHandler); 355 mHandler.post(() -> mBaseExecutor.execute(() -> 356 mBaseListener.onDiscoveryStarted(mServiceType))); 357 } 358 359 /** 360 * Stop discovery on all networks tracked by this class. 361 * 362 * This will request all underlying listeners to stop, and the last one to stop will call 363 * onDiscoveryStopped or onStopDiscoveryFailed. 364 * 365 * Must be called on the handler thread. 366 */ requestStop()367 public void requestStop() { 368 mHandler.post(() -> { 369 mStopRequested = true; 370 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 371 cm.unregisterNetworkCallback(mNetworkCb); 372 if (mPerNetworkListeners.size() == 0) { 373 mBaseExecutor.execute(() -> mBaseListener.onDiscoveryStopped(mServiceType)); 374 return; 375 } 376 for (int i = 0; i < mPerNetworkListeners.size(); i++) { 377 final DelegatingDiscoveryListener listener = mPerNetworkListeners.valueAt(i); 378 stopServiceDiscovery(listener); 379 } 380 }); 381 } 382 PerNetworkDiscoveryTracker(String serviceType, int protocolType, Executor baseExecutor, DiscoveryListener baseListener)383 private PerNetworkDiscoveryTracker(String serviceType, int protocolType, 384 Executor baseExecutor, DiscoveryListener baseListener) { 385 mServiceType = serviceType; 386 mProtocolType = protocolType; 387 mBaseExecutor = baseExecutor; 388 mBaseListener = baseListener; 389 } 390 391 /** 392 * Subset of NsdServiceInfo that is tracked to generate service lost notifications when a 393 * network is lost. 394 * 395 * Service lost notifications only contain service name, type and network, so only track 396 * that information (Network is known from the listener). This also implements 397 * equals/hashCode for usage in maps. 398 */ 399 private class TrackedNsdInfo { 400 private final String mServiceName; 401 private final String mServiceType; TrackedNsdInfo(NsdServiceInfo info)402 TrackedNsdInfo(NsdServiceInfo info) { 403 mServiceName = info.getServiceName(); 404 mServiceType = info.getServiceType(); 405 } 406 407 @Override hashCode()408 public int hashCode() { 409 return Objects.hash(mServiceName, mServiceType); 410 } 411 412 @Override equals(Object obj)413 public boolean equals(Object obj) { 414 if (!(obj instanceof TrackedNsdInfo)) return false; 415 final TrackedNsdInfo other = (TrackedNsdInfo) obj; 416 return Objects.equals(mServiceName, other.mServiceName) 417 && Objects.equals(mServiceType, other.mServiceType); 418 } 419 } 420 421 /** 422 * A listener wrapping calls to an app-provided listener, while keeping track of found 423 * services, so they can all be reported lost when the underlying network is lost. 424 * 425 * This should be registered to run on the service handler. 426 */ 427 private class DelegatingDiscoveryListener implements DiscoveryListener { 428 private final Network mNetwork; 429 private final DiscoveryListener mWrapped; 430 private final Executor mWrappedExecutor; 431 private final ArraySet<TrackedNsdInfo> mFoundInfo = new ArraySet<>(); 432 // When this flag is set to true, no further service found or lost callbacks should be 433 // handled. This flag indicates that the network for this DelegatingDiscoveryListener is 434 // lost, and any further callbacks would be redundant. 435 private boolean mAllServicesLost = false; 436 DelegatingDiscoveryListener(Network network, DiscoveryListener listener, Executor executor)437 private DelegatingDiscoveryListener(Network network, DiscoveryListener listener, 438 Executor executor) { 439 mNetwork = network; 440 mWrapped = listener; 441 mWrappedExecutor = executor; 442 } 443 notifyAllServicesLost()444 void notifyAllServicesLost() { 445 for (int i = 0; i < mFoundInfo.size(); i++) { 446 final TrackedNsdInfo trackedInfo = mFoundInfo.valueAt(i); 447 final NsdServiceInfo serviceInfo = new NsdServiceInfo( 448 trackedInfo.mServiceName, trackedInfo.mServiceType); 449 serviceInfo.setNetwork(mNetwork); 450 mWrappedExecutor.execute(() -> mWrapped.onServiceLost(serviceInfo)); 451 } 452 mAllServicesLost = true; 453 } 454 455 @Override onStartDiscoveryFailed(String serviceType, int errorCode)456 public void onStartDiscoveryFailed(String serviceType, int errorCode) { 457 // The delegated listener is used when NsdManager takes care of starting/stopping 458 // discovery on multiple networks. Failure to start on one network is not a global 459 // failure to be reported up, as other networks may succeed: just log. 460 Log.e(TAG, "Failed to start discovery for " + serviceType + " on " + mNetwork 461 + " with code " + errorCode); 462 mPerNetworkListeners.remove(mNetwork); 463 } 464 465 @Override onDiscoveryStarted(String serviceType)466 public void onDiscoveryStarted(String serviceType) { 467 // Wrapped listener was called upon registration, it is not called for discovery 468 // on each network 469 } 470 471 @Override onStopDiscoveryFailed(String serviceType, int errorCode)472 public void onStopDiscoveryFailed(String serviceType, int errorCode) { 473 Log.e(TAG, "Failed to stop discovery for " + serviceType + " on " + mNetwork 474 + " with code " + errorCode); 475 mPerNetworkListeners.remove(mNetwork); 476 if (mStopRequested && mPerNetworkListeners.size() == 0) { 477 // Do not report onStopDiscoveryFailed when some underlying listeners failed: 478 // this does not mean that all listeners did, and onStopDiscoveryFailed is not 479 // actionable anyway. Just report that discovery stopped. 480 mWrappedExecutor.execute(() -> mWrapped.onDiscoveryStopped(serviceType)); 481 } 482 } 483 484 @Override onDiscoveryStopped(String serviceType)485 public void onDiscoveryStopped(String serviceType) { 486 mPerNetworkListeners.remove(mNetwork); 487 if (mStopRequested && mPerNetworkListeners.size() == 0) { 488 mWrappedExecutor.execute(() -> mWrapped.onDiscoveryStopped(serviceType)); 489 } 490 } 491 492 @Override onServiceFound(NsdServiceInfo serviceInfo)493 public void onServiceFound(NsdServiceInfo serviceInfo) { 494 if (mAllServicesLost) { 495 // This DelegatingDiscoveryListener no longer has a network connection. Ignore 496 // the callback. 497 return; 498 } 499 mFoundInfo.add(new TrackedNsdInfo(serviceInfo)); 500 mWrappedExecutor.execute(() -> mWrapped.onServiceFound(serviceInfo)); 501 } 502 503 @Override onServiceLost(NsdServiceInfo serviceInfo)504 public void onServiceLost(NsdServiceInfo serviceInfo) { 505 if (mAllServicesLost) { 506 // This DelegatingDiscoveryListener no longer has a network connection. Ignore 507 // the callback. 508 return; 509 } 510 mFoundInfo.remove(new TrackedNsdInfo(serviceInfo)); 511 mWrappedExecutor.execute(() -> mWrapped.onServiceLost(serviceInfo)); 512 } 513 } 514 } 515 516 /** 517 * Create a new Nsd instance. Applications use 518 * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve 519 * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}. 520 * @param service the Binder interface 521 * @hide - hide this because it takes in a parameter of type INsdManager, which 522 * is a system private class. 523 */ NsdManager(Context context, INsdManager service)524 public NsdManager(Context context, INsdManager service) { 525 mContext = context; 526 527 HandlerThread t = new HandlerThread("NsdManager"); 528 t.start(); 529 mHandler = new ServiceHandler(t.getLooper()); 530 531 try { 532 mService = service.connect(new NsdCallbackImpl(mHandler), CompatChanges.isChangeEnabled( 533 ENABLE_PLATFORM_MDNS_BACKEND)); 534 } catch (RemoteException e) { 535 throw new RuntimeException("Failed to connect to NsdService"); 536 } 537 538 // Only proactively start the daemon if the target SDK < S, otherwise the internal service 539 // would automatically start/stop the native daemon as needed. 540 if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)) { 541 try { 542 mService.startDaemon(); 543 } catch (RemoteException e) { 544 Log.e(TAG, "Failed to proactively start daemon"); 545 // Continue: the daemon can still be started on-demand later 546 } 547 } 548 } 549 550 private static class NsdCallbackImpl extends INsdManagerCallback.Stub { 551 private final Handler mServHandler; 552 NsdCallbackImpl(Handler serviceHandler)553 NsdCallbackImpl(Handler serviceHandler) { 554 mServHandler = serviceHandler; 555 } 556 sendInfo(int message, int listenerKey, NsdServiceInfo info)557 private void sendInfo(int message, int listenerKey, NsdServiceInfo info) { 558 mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info)); 559 } 560 sendError(int message, int listenerKey, int error)561 private void sendError(int message, int listenerKey, int error) { 562 mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey)); 563 } 564 sendNoArg(int message, int listenerKey)565 private void sendNoArg(int message, int listenerKey) { 566 mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey)); 567 } 568 569 @Override onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info)570 public void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { 571 sendInfo(DISCOVER_SERVICES_STARTED, listenerKey, info); 572 } 573 574 @Override onDiscoverServicesFailed(int listenerKey, int error)575 public void onDiscoverServicesFailed(int listenerKey, int error) { 576 sendError(DISCOVER_SERVICES_FAILED, listenerKey, error); 577 } 578 579 @Override onServiceFound(int listenerKey, NsdServiceInfo info)580 public void onServiceFound(int listenerKey, NsdServiceInfo info) { 581 sendInfo(SERVICE_FOUND, listenerKey, info); 582 } 583 584 @Override onServiceLost(int listenerKey, NsdServiceInfo info)585 public void onServiceLost(int listenerKey, NsdServiceInfo info) { 586 sendInfo(SERVICE_LOST, listenerKey, info); 587 } 588 589 @Override onStopDiscoveryFailed(int listenerKey, int error)590 public void onStopDiscoveryFailed(int listenerKey, int error) { 591 sendError(STOP_DISCOVERY_FAILED, listenerKey, error); 592 } 593 594 @Override onStopDiscoverySucceeded(int listenerKey)595 public void onStopDiscoverySucceeded(int listenerKey) { 596 sendNoArg(STOP_DISCOVERY_SUCCEEDED, listenerKey); 597 } 598 599 @Override onRegisterServiceFailed(int listenerKey, int error)600 public void onRegisterServiceFailed(int listenerKey, int error) { 601 sendError(REGISTER_SERVICE_FAILED, listenerKey, error); 602 } 603 604 @Override onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info)605 public void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { 606 sendInfo(REGISTER_SERVICE_SUCCEEDED, listenerKey, info); 607 } 608 609 @Override onUnregisterServiceFailed(int listenerKey, int error)610 public void onUnregisterServiceFailed(int listenerKey, int error) { 611 sendError(UNREGISTER_SERVICE_FAILED, listenerKey, error); 612 } 613 614 @Override onUnregisterServiceSucceeded(int listenerKey)615 public void onUnregisterServiceSucceeded(int listenerKey) { 616 sendNoArg(UNREGISTER_SERVICE_SUCCEEDED, listenerKey); 617 } 618 619 @Override onResolveServiceFailed(int listenerKey, int error)620 public void onResolveServiceFailed(int listenerKey, int error) { 621 sendError(RESOLVE_SERVICE_FAILED, listenerKey, error); 622 } 623 624 @Override onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info)625 public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { 626 sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info); 627 } 628 629 @Override onStopResolutionFailed(int listenerKey, int error)630 public void onStopResolutionFailed(int listenerKey, int error) { 631 sendError(STOP_RESOLUTION_FAILED, listenerKey, error); 632 } 633 634 @Override onStopResolutionSucceeded(int listenerKey)635 public void onStopResolutionSucceeded(int listenerKey) { 636 sendNoArg(STOP_RESOLUTION_SUCCEEDED, listenerKey); 637 } 638 639 @Override onServiceInfoCallbackRegistrationFailed(int listenerKey, int error)640 public void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) { 641 sendError(REGISTER_SERVICE_CALLBACK_FAILED, listenerKey, error); 642 } 643 644 @Override onServiceUpdated(int listenerKey, NsdServiceInfo info)645 public void onServiceUpdated(int listenerKey, NsdServiceInfo info) { 646 sendInfo(SERVICE_UPDATED, listenerKey, info); 647 } 648 649 @Override onServiceUpdatedLost(int listenerKey)650 public void onServiceUpdatedLost(int listenerKey) { 651 sendNoArg(SERVICE_UPDATED_LOST, listenerKey); 652 } 653 654 @Override onServiceInfoCallbackUnregistered(int listenerKey)655 public void onServiceInfoCallbackUnregistered(int listenerKey) { 656 sendNoArg(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, listenerKey); 657 } 658 } 659 660 /** 661 * Failures are passed with {@link RegistrationListener#onRegistrationFailed}, 662 * {@link RegistrationListener#onUnregistrationFailed}, 663 * {@link DiscoveryListener#onStartDiscoveryFailed}, 664 * {@link DiscoveryListener#onStopDiscoveryFailed} or {@link ResolveListener#onResolveFailed}. 665 * 666 * Indicates that the operation failed due to an internal error. 667 */ 668 public static final int FAILURE_INTERNAL_ERROR = 0; 669 670 /** 671 * Indicates that the operation failed because it is already active. 672 */ 673 public static final int FAILURE_ALREADY_ACTIVE = 3; 674 675 /** 676 * Indicates that the operation failed because the maximum outstanding 677 * requests from the applications have reached. 678 */ 679 public static final int FAILURE_MAX_LIMIT = 4; 680 681 /** 682 * Indicates that the stop operation failed because it is not running. 683 * This failure is passed with {@link ResolveListener#onStopResolutionFailed}. 684 */ 685 public static final int FAILURE_OPERATION_NOT_RUNNING = 5; 686 687 /** 688 * Indicates that the service has failed to resolve because of bad parameters. 689 * 690 * This failure is passed with 691 * {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed}. 692 */ 693 public static final int FAILURE_BAD_PARAMETERS = 6; 694 695 /** @hide */ 696 @Retention(RetentionPolicy.SOURCE) 697 @IntDef(value = { 698 FAILURE_OPERATION_NOT_RUNNING, 699 }) 700 public @interface StopOperationFailureCode { 701 } 702 703 /** @hide */ 704 @Retention(RetentionPolicy.SOURCE) 705 @IntDef(value = { 706 FAILURE_ALREADY_ACTIVE, 707 FAILURE_BAD_PARAMETERS, 708 }) 709 public @interface ResolutionFailureCode { 710 } 711 712 /** Interface for callback invocation for service discovery */ 713 public interface DiscoveryListener { 714 onStartDiscoveryFailed(String serviceType, int errorCode)715 public void onStartDiscoveryFailed(String serviceType, int errorCode); 716 onStopDiscoveryFailed(String serviceType, int errorCode)717 public void onStopDiscoveryFailed(String serviceType, int errorCode); 718 onDiscoveryStarted(String serviceType)719 public void onDiscoveryStarted(String serviceType); 720 onDiscoveryStopped(String serviceType)721 public void onDiscoveryStopped(String serviceType); 722 onServiceFound(NsdServiceInfo serviceInfo)723 public void onServiceFound(NsdServiceInfo serviceInfo); 724 onServiceLost(NsdServiceInfo serviceInfo)725 public void onServiceLost(NsdServiceInfo serviceInfo); 726 } 727 728 /** Interface for callback invocation for service registration */ 729 public interface RegistrationListener { 730 onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode)731 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 732 onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode)733 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 734 onServiceRegistered(NsdServiceInfo serviceInfo)735 public void onServiceRegistered(NsdServiceInfo serviceInfo); 736 onServiceUnregistered(NsdServiceInfo serviceInfo)737 public void onServiceUnregistered(NsdServiceInfo serviceInfo); 738 } 739 740 /** 741 * Callback for use with {@link NsdManager#resolveService} to resolve the service info and use 742 * with {@link NsdManager#stopServiceResolution} to stop resolution. 743 */ 744 public interface ResolveListener { 745 746 /** 747 * Called on the internal thread or with an executor passed to 748 * {@link NsdManager#resolveService} to report the resolution was failed with an error. 749 * 750 * A resolution operation would call either onServiceResolved or onResolveFailed once based 751 * on the result. 752 */ onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)753 void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode); 754 755 /** 756 * Called on the internal thread or with an executor passed to 757 * {@link NsdManager#resolveService} to report the resolved service info. 758 * 759 * A resolution operation would call either onServiceResolved or onResolveFailed once based 760 * on the result. 761 */ onServiceResolved(NsdServiceInfo serviceInfo)762 void onServiceResolved(NsdServiceInfo serviceInfo); 763 764 /** 765 * Called on the internal thread or with an executor passed to 766 * {@link NsdManager#resolveService} to report the resolution was stopped. 767 * 768 * A stop resolution operation would call either onResolutionStopped or 769 * onStopResolutionFailed once based on the result. 770 */ onResolutionStopped(@onNull NsdServiceInfo serviceInfo)771 default void onResolutionStopped(@NonNull NsdServiceInfo serviceInfo) { } 772 773 /** 774 * Called once on the internal thread or with an executor passed to 775 * {@link NsdManager#resolveService} to report that stopping resolution failed with an 776 * error. 777 * 778 * A stop resolution operation would call either onResolutionStopped or 779 * onStopResolutionFailed once based on the result. 780 */ onStopResolutionFailed(@onNull NsdServiceInfo serviceInfo, @StopOperationFailureCode int errorCode)781 default void onStopResolutionFailed(@NonNull NsdServiceInfo serviceInfo, 782 @StopOperationFailureCode int errorCode) { } 783 } 784 785 /** 786 * Callback to listen to service info updates. 787 * 788 * For use with {@link NsdManager#registerServiceInfoCallback} to register, and with 789 * {@link NsdManager#unregisterServiceInfoCallback} to stop listening. 790 */ 791 public interface ServiceInfoCallback { 792 793 /** 794 * Reports that registering the callback failed with an error. 795 * 796 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. 797 * 798 * onServiceInfoCallbackRegistrationFailed will be called exactly once when the callback 799 * could not be registered. No other callback will be sent in that case. 800 */ onServiceInfoCallbackRegistrationFailed(@esolutionFailureCode int errorCode)801 void onServiceInfoCallbackRegistrationFailed(@ResolutionFailureCode int errorCode); 802 803 /** 804 * Reports updated service info. 805 * 806 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. Any 807 * service updates will be notified via this callback until 808 * {@link NsdManager#unregisterServiceInfoCallback} is called. This will only be called once 809 * the service is found, so may never be called if the service is never present. 810 */ onServiceUpdated(@onNull NsdServiceInfo serviceInfo)811 void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo); 812 813 /** 814 * Reports when the service that this callback listens to becomes unavailable. 815 * 816 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. The 817 * service may become available again, in which case {@link #onServiceUpdated} will be 818 * called. 819 */ onServiceLost()820 void onServiceLost(); 821 822 /** 823 * Reports that service info updates have stopped. 824 * 825 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. 826 * 827 * A callback unregistration operation will call onServiceInfoCallbackUnregistered 828 * once. After this, the callback may be reused. 829 */ onServiceInfoCallbackUnregistered()830 void onServiceInfoCallbackUnregistered(); 831 } 832 833 @VisibleForTesting 834 class ServiceHandler extends Handler { ServiceHandler(Looper looper)835 ServiceHandler(Looper looper) { 836 super(looper); 837 } 838 839 @Override handleMessage(Message message)840 public void handleMessage(Message message) { 841 // Do not use message in the executor lambdas, as it will be recycled once this method 842 // returns. Keep references to its content instead. 843 final int what = message.what; 844 final int errorCode = message.arg1; 845 final int key = message.arg2; 846 final Object obj = message.obj; 847 final Object listener; 848 final NsdServiceInfo ns; 849 final Executor executor; 850 synchronized (mMapLock) { 851 listener = mListenerMap.get(key); 852 ns = mServiceMap.get(key); 853 executor = mExecutorMap.get(key); 854 } 855 if (listener == null) { 856 Log.d(TAG, "Stale key " + key); 857 return; 858 } 859 if (DBG) { 860 Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns); 861 } 862 switch (what) { 863 case DISCOVER_SERVICES_STARTED: 864 final String s = getNsdServiceInfoType((NsdServiceInfo) obj); 865 executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStarted(s)); 866 break; 867 case DISCOVER_SERVICES_FAILED: 868 removeListener(key); 869 executor.execute(() -> ((DiscoveryListener) listener).onStartDiscoveryFailed( 870 getNsdServiceInfoType(ns), errorCode)); 871 break; 872 case SERVICE_FOUND: 873 executor.execute(() -> ((DiscoveryListener) listener).onServiceFound( 874 (NsdServiceInfo) obj)); 875 break; 876 case SERVICE_LOST: 877 executor.execute(() -> ((DiscoveryListener) listener).onServiceLost( 878 (NsdServiceInfo) obj)); 879 break; 880 case STOP_DISCOVERY_FAILED: 881 // TODO: failure to stop discovery should be internal and retried internally, as 882 // the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED 883 removeListener(key); 884 executor.execute(() -> ((DiscoveryListener) listener).onStopDiscoveryFailed( 885 getNsdServiceInfoType(ns), errorCode)); 886 break; 887 case STOP_DISCOVERY_SUCCEEDED: 888 removeListener(key); 889 executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStopped( 890 getNsdServiceInfoType(ns))); 891 break; 892 case REGISTER_SERVICE_FAILED: 893 removeListener(key); 894 executor.execute(() -> ((RegistrationListener) listener).onRegistrationFailed( 895 ns, errorCode)); 896 break; 897 case REGISTER_SERVICE_SUCCEEDED: 898 executor.execute(() -> ((RegistrationListener) listener).onServiceRegistered( 899 (NsdServiceInfo) obj)); 900 break; 901 case UNREGISTER_SERVICE_FAILED: 902 removeListener(key); 903 executor.execute(() -> ((RegistrationListener) listener).onUnregistrationFailed( 904 ns, errorCode)); 905 break; 906 case UNREGISTER_SERVICE_SUCCEEDED: 907 // TODO: do not unregister listener until service is unregistered, or provide 908 // alternative way for unregistering ? 909 removeListener(key); 910 executor.execute(() -> ((RegistrationListener) listener).onServiceUnregistered( 911 ns)); 912 break; 913 case RESOLVE_SERVICE_FAILED: 914 removeListener(key); 915 executor.execute(() -> ((ResolveListener) listener).onResolveFailed( 916 ns, errorCode)); 917 break; 918 case RESOLVE_SERVICE_SUCCEEDED: 919 removeListener(key); 920 executor.execute(() -> ((ResolveListener) listener).onServiceResolved( 921 (NsdServiceInfo) obj)); 922 break; 923 case STOP_RESOLUTION_FAILED: 924 removeListener(key); 925 executor.execute(() -> ((ResolveListener) listener).onStopResolutionFailed( 926 ns, errorCode)); 927 break; 928 case STOP_RESOLUTION_SUCCEEDED: 929 removeListener(key); 930 executor.execute(() -> ((ResolveListener) listener).onResolutionStopped( 931 ns)); 932 break; 933 case REGISTER_SERVICE_CALLBACK_FAILED: 934 removeListener(key); 935 executor.execute(() -> ((ServiceInfoCallback) listener) 936 .onServiceInfoCallbackRegistrationFailed(errorCode)); 937 break; 938 case SERVICE_UPDATED: 939 executor.execute(() -> ((ServiceInfoCallback) listener) 940 .onServiceUpdated((NsdServiceInfo) obj)); 941 break; 942 case SERVICE_UPDATED_LOST: 943 executor.execute(() -> ((ServiceInfoCallback) listener).onServiceLost()); 944 break; 945 case UNREGISTER_SERVICE_CALLBACK_SUCCEEDED: 946 removeListener(key); 947 executor.execute(() -> ((ServiceInfoCallback) listener) 948 .onServiceInfoCallbackUnregistered()); 949 break; 950 default: 951 Log.d(TAG, "Ignored " + message); 952 break; 953 } 954 } 955 } 956 nextListenerKey()957 private int nextListenerKey() { 958 // Ensure mListenerKey >= FIRST_LISTENER_KEY; 959 mListenerKey = Math.max(FIRST_LISTENER_KEY, mListenerKey + 1); 960 return mListenerKey; 961 } 962 963 // Assert that the listener is not in the map, then add it and returns its key putListener(Object listener, Executor e, NsdServiceInfo s)964 private int putListener(Object listener, Executor e, NsdServiceInfo s) { 965 checkListener(listener); 966 final int key; 967 synchronized (mMapLock) { 968 int valueIndex = mListenerMap.indexOfValue(listener); 969 if (valueIndex != -1) { 970 throw new IllegalArgumentException("listener already in use"); 971 } 972 key = nextListenerKey(); 973 mListenerMap.put(key, listener); 974 mServiceMap.put(key, s); 975 mExecutorMap.put(key, e); 976 } 977 return key; 978 } 979 removeListener(int key)980 private void removeListener(int key) { 981 synchronized (mMapLock) { 982 mListenerMap.remove(key); 983 mServiceMap.remove(key); 984 mExecutorMap.remove(key); 985 } 986 } 987 getListenerKey(Object listener)988 private int getListenerKey(Object listener) { 989 checkListener(listener); 990 synchronized (mMapLock) { 991 int valueIndex = mListenerMap.indexOfValue(listener); 992 if (valueIndex == -1) { 993 throw new IllegalArgumentException("listener not registered"); 994 } 995 return mListenerMap.keyAt(valueIndex); 996 } 997 } 998 getNsdServiceInfoType(NsdServiceInfo s)999 private static String getNsdServiceInfoType(NsdServiceInfo s) { 1000 if (s == null) return "?"; 1001 return s.getServiceType(); 1002 } 1003 1004 /** 1005 * Register a service to be discovered by other services. 1006 * 1007 * <p> The function call immediately returns after sending a request to register service 1008 * to the framework. The application is notified of a successful registration 1009 * through the callback {@link RegistrationListener#onServiceRegistered} or a failure 1010 * through {@link RegistrationListener#onRegistrationFailed}. 1011 * 1012 * <p> The application should call {@link #unregisterService} when the service 1013 * registration is no longer required, and/or whenever the application is stopped. 1014 * 1015 * @param serviceInfo The service being registered 1016 * @param protocolType The service discovery protocol 1017 * @param listener The listener notifies of a successful registration and is used to 1018 * unregister this service through a call on {@link #unregisterService}. Cannot be null. 1019 * Cannot be in use for an active service registration. 1020 */ registerService(NsdServiceInfo serviceInfo, int protocolType, RegistrationListener listener)1021 public void registerService(NsdServiceInfo serviceInfo, int protocolType, 1022 RegistrationListener listener) { 1023 registerService(serviceInfo, protocolType, Runnable::run, listener); 1024 } 1025 1026 /** 1027 * Register a service to be discovered by other services. 1028 * 1029 * <p> The function call immediately returns after sending a request to register service 1030 * to the framework. The application is notified of a successful registration 1031 * through the callback {@link RegistrationListener#onServiceRegistered} or a failure 1032 * through {@link RegistrationListener#onRegistrationFailed}. 1033 * 1034 * <p> The application should call {@link #unregisterService} when the service 1035 * registration is no longer required, and/or whenever the application is stopped. 1036 * @param serviceInfo The service being registered 1037 * @param protocolType The service discovery protocol 1038 * @param executor Executor to run listener callbacks with 1039 * @param listener The listener notifies of a successful registration and is used to 1040 * unregister this service through a call on {@link #unregisterService}. Cannot be null. 1041 */ registerService(@onNull NsdServiceInfo serviceInfo, int protocolType, @NonNull Executor executor, @NonNull RegistrationListener listener)1042 public void registerService(@NonNull NsdServiceInfo serviceInfo, int protocolType, 1043 @NonNull Executor executor, @NonNull RegistrationListener listener) { 1044 if (serviceInfo.getPort() <= 0) { 1045 throw new IllegalArgumentException("Invalid port number"); 1046 } 1047 checkServiceInfo(serviceInfo); 1048 checkProtocol(protocolType); 1049 int key = putListener(listener, executor, serviceInfo); 1050 try { 1051 mService.registerService(key, serviceInfo); 1052 } catch (RemoteException e) { 1053 e.rethrowFromSystemServer(); 1054 } 1055 } 1056 1057 /** 1058 * Unregister a service registered through {@link #registerService}. A successful 1059 * unregister is notified to the application with a call to 1060 * {@link RegistrationListener#onServiceUnregistered}. 1061 * 1062 * @param listener This should be the listener object that was passed to 1063 * {@link #registerService}. It identifies the service that should be unregistered 1064 * and notifies of a successful or unsuccessful unregistration via the listener 1065 * callbacks. In API versions 20 and above, the listener object may be used for 1066 * another service registration once the callback has been called. In API versions <= 19, 1067 * there is no entirely reliable way to know when a listener may be re-used, and a new 1068 * listener should be created for each service registration request. 1069 */ unregisterService(RegistrationListener listener)1070 public void unregisterService(RegistrationListener listener) { 1071 int id = getListenerKey(listener); 1072 try { 1073 mService.unregisterService(id); 1074 } catch (RemoteException e) { 1075 e.rethrowFromSystemServer(); 1076 } 1077 } 1078 1079 /** 1080 * Initiate service discovery to browse for instances of a service type. Service discovery 1081 * consumes network bandwidth and will continue until the application calls 1082 * {@link #stopServiceDiscovery}. 1083 * 1084 * <p> The function call immediately returns after sending a request to start service 1085 * discovery to the framework. The application is notified of a success to initiate 1086 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 1087 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 1088 * 1089 * <p> Upon successful start, application is notified when a service is found with 1090 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 1091 * {@link DiscoveryListener#onServiceLost}. 1092 * 1093 * <p> Upon failure to start, service discovery is not active and application does 1094 * not need to invoke {@link #stopServiceDiscovery} 1095 * 1096 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 1097 * service type is no longer required, and/or whenever the application is paused or 1098 * stopped. 1099 * 1100 * @param serviceType The service type being discovered. Examples include "_http._tcp" for 1101 * http services or "_ipp._tcp" for printers 1102 * @param protocolType The service discovery protocol 1103 * @param listener The listener notifies of a successful discovery and is used 1104 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 1105 * Cannot be null. Cannot be in use for an active service discovery. 1106 */ discoverServices(String serviceType, int protocolType, DiscoveryListener listener)1107 public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { 1108 discoverServices(serviceType, protocolType, (Network) null, Runnable::run, listener); 1109 } 1110 1111 /** 1112 * Initiate service discovery to browse for instances of a service type. Service discovery 1113 * consumes network bandwidth and will continue until the application calls 1114 * {@link #stopServiceDiscovery}. 1115 * 1116 * <p> The function call immediately returns after sending a request to start service 1117 * discovery to the framework. The application is notified of a success to initiate 1118 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 1119 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 1120 * 1121 * <p> Upon successful start, application is notified when a service is found with 1122 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 1123 * {@link DiscoveryListener#onServiceLost}. 1124 * 1125 * <p> Upon failure to start, service discovery is not active and application does 1126 * not need to invoke {@link #stopServiceDiscovery} 1127 * 1128 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 1129 * service type is no longer required, and/or whenever the application is paused or 1130 * stopped. 1131 * @param serviceType The service type being discovered. Examples include "_http._tcp" for 1132 * http services or "_ipp._tcp" for printers 1133 * @param protocolType The service discovery protocol 1134 * @param network Network to discover services on, or null to discover on all available networks 1135 * @param executor Executor to run listener callbacks with 1136 * @param listener The listener notifies of a successful discovery and is used 1137 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 1138 */ discoverServices(@onNull String serviceType, int protocolType, @Nullable Network network, @NonNull Executor executor, @NonNull DiscoveryListener listener)1139 public void discoverServices(@NonNull String serviceType, int protocolType, 1140 @Nullable Network network, @NonNull Executor executor, 1141 @NonNull DiscoveryListener listener) { 1142 if (TextUtils.isEmpty(serviceType)) { 1143 throw new IllegalArgumentException("Service type cannot be empty"); 1144 } 1145 checkProtocol(protocolType); 1146 1147 NsdServiceInfo s = new NsdServiceInfo(); 1148 s.setServiceType(serviceType); 1149 s.setNetwork(network); 1150 1151 int key = putListener(listener, executor, s); 1152 try { 1153 mService.discoverServices(key, s); 1154 } catch (RemoteException e) { 1155 e.rethrowFromSystemServer(); 1156 } 1157 } 1158 1159 /** 1160 * Initiate service discovery to browse for instances of a service type. Service discovery 1161 * consumes network bandwidth and will continue until the application calls 1162 * {@link #stopServiceDiscovery}. 1163 * 1164 * <p> The function call immediately returns after sending a request to start service 1165 * discovery to the framework. The application is notified of a success to initiate 1166 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 1167 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 1168 * 1169 * <p> Upon successful start, application is notified when a service is found with 1170 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 1171 * {@link DiscoveryListener#onServiceLost}. 1172 * 1173 * <p> Upon failure to start, service discovery is not active and application does 1174 * not need to invoke {@link #stopServiceDiscovery} 1175 * 1176 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 1177 * service type is no longer required, and/or whenever the application is paused or 1178 * stopped. 1179 * 1180 * <p> During discovery, new networks may connect or existing networks may disconnect - for 1181 * example if wifi is reconnected. When a service was found on a network that disconnects, 1182 * {@link DiscoveryListener#onServiceLost} will be called. If a new network connects that 1183 * matches the {@link NetworkRequest}, {@link DiscoveryListener#onServiceFound} will be called 1184 * for services found on that network. Applications that do not want to track networks 1185 * themselves are encouraged to use this method instead of other overloads of 1186 * {@code discoverServices}, as they will receive proper notifications when a service becomes 1187 * available or unavailable due to network changes. 1188 * @param serviceType The service type being discovered. Examples include "_http._tcp" for 1189 * http services or "_ipp._tcp" for printers 1190 * @param protocolType The service discovery protocol 1191 * @param networkRequest Request specifying networks that should be considered when discovering 1192 * @param executor Executor to run listener callbacks with 1193 * @param listener The listener notifies of a successful discovery and is used 1194 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 1195 */ 1196 @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) discoverServices(@onNull String serviceType, int protocolType, @NonNull NetworkRequest networkRequest, @NonNull Executor executor, @NonNull DiscoveryListener listener)1197 public void discoverServices(@NonNull String serviceType, int protocolType, 1198 @NonNull NetworkRequest networkRequest, @NonNull Executor executor, 1199 @NonNull DiscoveryListener listener) { 1200 if (TextUtils.isEmpty(serviceType)) { 1201 throw new IllegalArgumentException("Service type cannot be empty"); 1202 } 1203 Objects.requireNonNull(networkRequest, "NetworkRequest cannot be null"); 1204 checkProtocol(protocolType); 1205 1206 NsdServiceInfo s = new NsdServiceInfo(); 1207 s.setServiceType(serviceType); 1208 1209 final int baseListenerKey = putListener(listener, executor, s); 1210 1211 final PerNetworkDiscoveryTracker discoveryInfo = new PerNetworkDiscoveryTracker( 1212 serviceType, protocolType, executor, listener); 1213 1214 synchronized (mPerNetworkDiscoveryMap) { 1215 mPerNetworkDiscoveryMap.put(baseListenerKey, discoveryInfo); 1216 discoveryInfo.start(networkRequest); 1217 } 1218 } 1219 1220 /** 1221 * Stop service discovery initiated with {@link #discoverServices}. An active service 1222 * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted} 1223 * and it stays active until the application invokes a stop service discovery. A successful 1224 * stop is notified to with a call to {@link DiscoveryListener#onDiscoveryStopped}. 1225 * 1226 * <p> Upon failure to stop service discovery, application is notified through 1227 * {@link DiscoveryListener#onStopDiscoveryFailed}. 1228 * 1229 * @param listener This should be the listener object that was passed to {@link #discoverServices}. 1230 * It identifies the discovery that should be stopped and notifies of a successful or 1231 * unsuccessful stop. In API versions 20 and above, the listener object may be used for 1232 * another service discovery once the callback has been called. In API versions <= 19, 1233 * there is no entirely reliable way to know when a listener may be re-used, and a new 1234 * listener should be created for each service discovery request. 1235 */ stopServiceDiscovery(DiscoveryListener listener)1236 public void stopServiceDiscovery(DiscoveryListener listener) { 1237 int id = getListenerKey(listener); 1238 // If this is a PerNetworkDiscovery request, handle it as such 1239 synchronized (mPerNetworkDiscoveryMap) { 1240 final PerNetworkDiscoveryTracker info = mPerNetworkDiscoveryMap.get(id); 1241 if (info != null) { 1242 info.requestStop(); 1243 return; 1244 } 1245 } 1246 try { 1247 mService.stopDiscovery(id); 1248 } catch (RemoteException e) { 1249 e.rethrowFromSystemServer(); 1250 } 1251 } 1252 1253 /** 1254 * Resolve a discovered service. An application can resolve a service right before 1255 * establishing a connection to fetch the IP and port details on which to setup 1256 * the connection. 1257 * 1258 * @param serviceInfo service to be resolved 1259 * @param listener to receive callback upon success or failure. Cannot be null. 1260 * Cannot be in use for an active service resolution. 1261 * 1262 * @deprecated the returned ServiceInfo may get stale at any time after resolution, including 1263 * immediately after the callback is called, and may not contain some service information that 1264 * could be delivered later, like additional host addresses. Prefer using 1265 * {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the 1266 * state of the service. 1267 */ 1268 @Deprecated resolveService(NsdServiceInfo serviceInfo, ResolveListener listener)1269 public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { 1270 resolveService(serviceInfo, Runnable::run, listener); 1271 } 1272 1273 /** 1274 * Resolve a discovered service. An application can resolve a service right before 1275 * establishing a connection to fetch the IP and port details on which to setup 1276 * the connection. 1277 * @param serviceInfo service to be resolved 1278 * @param executor Executor to run listener callbacks with 1279 * @param listener to receive callback upon success or failure. 1280 * 1281 * @deprecated the returned ServiceInfo may get stale at any time after resolution, including 1282 * immediately after the callback is called, and may not contain some service information that 1283 * could be delivered later, like additional host addresses. Prefer using 1284 * {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the 1285 * state of the service. 1286 */ 1287 @Deprecated resolveService(@onNull NsdServiceInfo serviceInfo, @NonNull Executor executor, @NonNull ResolveListener listener)1288 public void resolveService(@NonNull NsdServiceInfo serviceInfo, 1289 @NonNull Executor executor, @NonNull ResolveListener listener) { 1290 checkServiceInfo(serviceInfo); 1291 int key = putListener(listener, executor, serviceInfo); 1292 try { 1293 mService.resolveService(key, serviceInfo); 1294 } catch (RemoteException e) { 1295 e.rethrowFromSystemServer(); 1296 } 1297 } 1298 1299 /** 1300 * Stop service resolution initiated with {@link #resolveService}. 1301 * 1302 * A successful stop is notified with a call to {@link ResolveListener#onResolutionStopped}. 1303 * 1304 * <p> Upon failure to stop service resolution for example if resolution is done or the 1305 * requester stops resolution repeatedly, the application is notified 1306 * {@link ResolveListener#onStopResolutionFailed} with {@link #FAILURE_OPERATION_NOT_RUNNING} 1307 * 1308 * @param listener This should be a listener object that was passed to {@link #resolveService}. 1309 * It identifies the resolution that should be stopped and notifies of a 1310 * successful or unsuccessful stop. Throws {@code IllegalArgumentException} if 1311 * the listener was not passed to resolveService before. 1312 */ stopServiceResolution(@onNull ResolveListener listener)1313 public void stopServiceResolution(@NonNull ResolveListener listener) { 1314 int id = getListenerKey(listener); 1315 try { 1316 mService.stopResolution(id); 1317 } catch (RemoteException e) { 1318 e.rethrowFromSystemServer(); 1319 } 1320 } 1321 1322 /** 1323 * Register a callback to listen for updates to a service. 1324 * 1325 * An application can listen to a service to continuously monitor availability of given service. 1326 * The callback methods will be called on the passed executor. And service updates are sent with 1327 * continuous calls to {@link ServiceInfoCallback#onServiceUpdated}. 1328 * 1329 * This is different from {@link #resolveService} which provides one shot service information. 1330 * 1331 * <p> An application can listen to a service once a time. It needs to cancel the registration 1332 * before registering other callbacks. Upon failure to register a callback for example if 1333 * it's a duplicated registration, the application is notified through 1334 * {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed} with 1335 * {@link #FAILURE_BAD_PARAMETERS}. 1336 * 1337 * @param serviceInfo the service to receive updates for 1338 * @param executor Executor to run callbacks with 1339 * @param listener to receive callback upon service update 1340 */ registerServiceInfoCallback(@onNull NsdServiceInfo serviceInfo, @NonNull Executor executor, @NonNull ServiceInfoCallback listener)1341 public void registerServiceInfoCallback(@NonNull NsdServiceInfo serviceInfo, 1342 @NonNull Executor executor, @NonNull ServiceInfoCallback listener) { 1343 checkServiceInfo(serviceInfo); 1344 int key = putListener(listener, executor, serviceInfo); 1345 try { 1346 mService.registerServiceInfoCallback(key, serviceInfo); 1347 } catch (RemoteException e) { 1348 e.rethrowFromSystemServer(); 1349 } 1350 } 1351 1352 /** 1353 * Unregister a callback registered with {@link #registerServiceInfoCallback}. 1354 * 1355 * A successful unregistration is notified with a call to 1356 * {@link ServiceInfoCallback#onServiceInfoCallbackUnregistered}. The same callback can only be 1357 * reused after this is called. 1358 * 1359 * <p>If the callback is not already registered, this will throw with 1360 * {@link IllegalArgumentException}. 1361 * 1362 * @param listener This should be a listener object that was passed to 1363 * {@link #registerServiceInfoCallback}. It identifies the registration that 1364 * should be unregistered and notifies of a successful or unsuccessful stop. 1365 * Throws {@code IllegalArgumentException} if the listener was not passed to 1366 * {@link #registerServiceInfoCallback} before. 1367 */ unregisterServiceInfoCallback(@onNull ServiceInfoCallback listener)1368 public void unregisterServiceInfoCallback(@NonNull ServiceInfoCallback listener) { 1369 // Will throw IllegalArgumentException if the listener is not known 1370 int id = getListenerKey(listener); 1371 try { 1372 mService.unregisterServiceInfoCallback(id); 1373 } catch (RemoteException e) { 1374 e.rethrowFromSystemServer(); 1375 } 1376 } 1377 checkListener(Object listener)1378 private static void checkListener(Object listener) { 1379 Objects.requireNonNull(listener, "listener cannot be null"); 1380 } 1381 checkProtocol(int protocolType)1382 private static void checkProtocol(int protocolType) { 1383 if (protocolType != PROTOCOL_DNS_SD) { 1384 throw new IllegalArgumentException("Unsupported protocol"); 1385 } 1386 } 1387 checkServiceInfo(NsdServiceInfo serviceInfo)1388 private static void checkServiceInfo(NsdServiceInfo serviceInfo) { 1389 Objects.requireNonNull(serviceInfo, "NsdServiceInfo cannot be null"); 1390 if (TextUtils.isEmpty(serviceInfo.getServiceName())) { 1391 throw new IllegalArgumentException("Service name cannot be empty"); 1392 } 1393 if (TextUtils.isEmpty(serviceInfo.getServiceType())) { 1394 throw new IllegalArgumentException("Service type cannot be empty"); 1395 } 1396 } 1397 } 1398