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.Manifest.permission.NETWORK_SETTINGS; 20 import static android.Manifest.permission.NETWORK_STACK; 21 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; 22 import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND; 23 import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER; 24 25 import android.annotation.FlaggedApi; 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.RequiresPermission; 30 import android.annotation.SdkConstant; 31 import android.annotation.SdkConstant.SdkConstantType; 32 import android.annotation.SystemApi; 33 import android.annotation.SystemService; 34 import android.app.compat.CompatChanges; 35 import android.content.Context; 36 import android.net.ConnectivityManager; 37 import android.net.ConnectivityManager.NetworkCallback; 38 import android.net.ConnectivityThread; 39 import android.net.Network; 40 import android.net.NetworkRequest; 41 import android.os.Handler; 42 import android.os.Looper; 43 import android.os.Message; 44 import android.os.RemoteException; 45 import android.text.TextUtils; 46 import android.util.ArrayMap; 47 import android.util.ArraySet; 48 import android.util.Log; 49 import android.util.Pair; 50 import android.util.SparseArray; 51 52 import com.android.internal.annotations.GuardedBy; 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.modules.utils.build.SdkLevel; 55 import com.android.net.module.util.CollectionUtils; 56 57 import java.lang.annotation.Retention; 58 import java.lang.annotation.RetentionPolicy; 59 import java.util.ArrayList; 60 import java.util.Objects; 61 import java.util.concurrent.Executor; 62 import java.util.regex.Matcher; 63 import java.util.regex.Pattern; 64 65 /** 66 * The Network Service Discovery Manager class provides the API to discover services 67 * on a network. As an example, if device A and device B are connected over a Wi-Fi 68 * network, a game registered on device A can be discovered by a game on device 69 * B. Another example use case is an application discovering printers on the network. 70 * 71 * <p> The API currently supports DNS based service discovery and discovery is currently 72 * limited to a local network over Multicast DNS. DNS service discovery is described at 73 * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt 74 * 75 * <p> The API is asynchronous, and responses to requests from an application are on listener 76 * callbacks on a separate internal thread. 77 * 78 * <p> There are three main operations the API supports - registration, discovery and resolution. 79 * <pre> 80 * Application start 81 * | 82 * | 83 * | onServiceRegistered() 84 * Register any local services / 85 * to be advertised with \ 86 * registerService() onRegistrationFailed() 87 * | 88 * | 89 * discoverServices() 90 * | 91 * Maintain a list to track 92 * discovered services 93 * | 94 * |---------> 95 * | | 96 * | onServiceFound() 97 * | | 98 * | add service to list 99 * | | 100 * |<---------- 101 * | 102 * |---------> 103 * | | 104 * | onServiceLost() 105 * | | 106 * | remove service from list 107 * | | 108 * |<---------- 109 * | 110 * | 111 * | Connect to a service 112 * | from list ? 113 * | 114 * resolveService() 115 * | 116 * onServiceResolved() 117 * | 118 * Establish connection to service 119 * with the host and port information 120 * 121 * </pre> 122 * An application that needs to advertise itself over a network for other applications to 123 * discover it can do so with a call to {@link #registerService}. If Example is a http based 124 * application that can provide HTML data to peer services, it can register a name "Example" 125 * with service type "_http._tcp". A successful registration is notified with a callback to 126 * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified 127 * over {@link RegistrationListener#onRegistrationFailed} 128 * 129 * <p> A peer application looking for http services can initiate a discovery for "_http._tcp" 130 * with a call to {@link #discoverServices}. A service found is notified with a callback 131 * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on 132 * {@link DiscoveryListener#onServiceLost}. 133 * 134 * <p> Once the peer application discovers the "Example" http service, and either needs to read the 135 * attributes of the service or wants to receive data from the "Example" application, it can 136 * initiate a resolve with {@link #resolveService} to resolve the attributes, host, and port 137 * details. A successful resolve is notified on {@link ResolveListener#onServiceResolved} and a 138 * failure is notified on {@link ResolveListener#onResolveFailed}. 139 * 140 * Applications can reserve for a service type at 141 * http://www.iana.org/form/ports-service. Existing services can be found at 142 * http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml 143 * 144 * @see NsdServiceInfo 145 */ 146 @SystemService(Context.NSD_SERVICE) 147 public final class NsdManager { 148 private static final String TAG = NsdManager.class.getSimpleName(); 149 private static final boolean DBG = false; 150 151 // TODO : remove this class when udc-mainline-prod is abandoned and android.net.flags.Flags is 152 // available here 153 /** @hide */ 154 public static class Flags { 155 static final String REGISTER_NSD_OFFLOAD_ENGINE_API = 156 "com.android.net.flags.register_nsd_offload_engine_api"; 157 static final String NSD_SUBTYPES_SUPPORT_ENABLED = 158 "com.android.net.flags.nsd_subtypes_support_enabled"; 159 static final String ADVERTISE_REQUEST_API = 160 "com.android.net.flags.advertise_request_api"; 161 static final String NSD_CUSTOM_HOSTNAME_ENABLED = 162 "com.android.net.flags.nsd_custom_hostname_enabled"; 163 static final String NSD_CUSTOM_TTL_ENABLED = 164 "com.android.net.flags.nsd_custom_ttl_enabled"; 165 } 166 167 /** 168 * A regex for the acceptable format of a type or subtype label. 169 * @hide 170 */ 171 public static final String TYPE_LABEL_REGEX = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]"; 172 173 /** 174 * A regex for the acceptable format of a subtype label. 175 * 176 * As per RFC 6763 7.1, "Subtype strings are not required to begin with an underscore, though 177 * they often do.", and "Subtype strings [...] may be constructed using arbitrary 8-bit data 178 * values. In many cases these data values may be UTF-8 [RFC3629] representations of text, or 179 * even (as in the example above) plain ASCII [RFC20], but they do not have to be.". 180 * 181 * This regex is overly conservative as it mandates the underscore and only allows printable 182 * ASCII characters (codes 0x20 to 0x7e, space to tilde), except for comma (0x2c) and dot 183 * (0x2e); so the NsdManager API does not allow everything the RFC allows. This may be revisited 184 * in the future, but using arbitrary bytes makes logging and testing harder, and using other 185 * characters would probably be a bad idea for interoperability for apps. 186 * @hide 187 */ 188 public static final String SUBTYPE_LABEL_REGEX = "_[" 189 + "\\x20-\\x2b" 190 + "\\x2d" 191 + "\\x2f-\\x7e" 192 + "]{1,62}"; 193 194 /** 195 * A regex for the acceptable format of a service type specification. 196 * 197 * When it matches, matcher group 1 is an optional leading subtype when using legacy dot syntax 198 * (_subtype._type._tcp). Matcher group 2 is the actual type, and matcher group 3 contains 199 * optional comma-separated subtypes. 200 * @hide 201 */ 202 public static final String TYPE_REGEX = 203 // Optional leading subtype (_subtype._type._tcp) 204 // (?: xxx) is a non-capturing parenthesis, don't capture the dot 205 "^(?:(" + SUBTYPE_LABEL_REGEX + ")\\.)?" 206 // Actual type (_type._tcp.local) 207 + "(" + TYPE_LABEL_REGEX + "\\._(?:tcp|udp))" 208 // Drop '.' at the end of service type that is compatible with old backend. 209 // e.g. allow "_type._tcp.local." 210 + "\\.?" 211 // Optional subtype after comma, for "_type._tcp,_subtype1,_subtype2" format 212 + "((?:," + SUBTYPE_LABEL_REGEX + ")*)" 213 + "$"; 214 215 /** 216 * Broadcast intent action to indicate whether network service discovery is 217 * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state 218 * information as int. 219 * 220 * @see #EXTRA_NSD_STATE 221 */ 222 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 223 public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED"; 224 225 /** 226 * The lookup key for an int that indicates whether network service discovery is enabled 227 * or disabled. Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. 228 * 229 * @see #NSD_STATE_DISABLED 230 * @see #NSD_STATE_ENABLED 231 */ 232 public static final String EXTRA_NSD_STATE = "nsd_state"; 233 234 /** 235 * Network service discovery is disabled 236 * 237 * @see #ACTION_NSD_STATE_CHANGED 238 */ 239 // TODO: Deprecate this since NSD service is never disabled. 240 public static final int NSD_STATE_DISABLED = 1; 241 242 /** 243 * Network service discovery is enabled 244 * 245 * @see #ACTION_NSD_STATE_CHANGED 246 */ 247 public static final int NSD_STATE_ENABLED = 2; 248 249 /** @hide */ 250 public static final int DISCOVER_SERVICES = 1; 251 /** @hide */ 252 public static final int DISCOVER_SERVICES_STARTED = 2; 253 /** @hide */ 254 public static final int DISCOVER_SERVICES_FAILED = 3; 255 /** @hide */ 256 public static final int SERVICE_FOUND = 4; 257 /** @hide */ 258 public static final int SERVICE_LOST = 5; 259 260 /** @hide */ 261 public static final int STOP_DISCOVERY = 6; 262 /** @hide */ 263 public static final int STOP_DISCOVERY_FAILED = 7; 264 /** @hide */ 265 public static final int STOP_DISCOVERY_SUCCEEDED = 8; 266 267 /** @hide */ 268 public static final int REGISTER_SERVICE = 9; 269 /** @hide */ 270 public static final int REGISTER_SERVICE_FAILED = 10; 271 /** @hide */ 272 public static final int REGISTER_SERVICE_SUCCEEDED = 11; 273 274 /** @hide */ 275 public static final int UNREGISTER_SERVICE = 12; 276 /** @hide */ 277 public static final int UNREGISTER_SERVICE_FAILED = 13; 278 /** @hide */ 279 public static final int UNREGISTER_SERVICE_SUCCEEDED = 14; 280 281 /** @hide */ 282 public static final int RESOLVE_SERVICE = 15; 283 /** @hide */ 284 public static final int RESOLVE_SERVICE_FAILED = 16; 285 /** @hide */ 286 public static final int RESOLVE_SERVICE_SUCCEEDED = 17; 287 288 /** @hide */ 289 public static final int DAEMON_CLEANUP = 18; 290 /** @hide */ 291 public static final int DAEMON_STARTUP = 19; 292 293 /** @hide */ 294 public static final int MDNS_SERVICE_EVENT = 20; 295 296 /** @hide */ 297 public static final int REGISTER_CLIENT = 21; 298 /** @hide */ 299 public static final int UNREGISTER_CLIENT = 22; 300 301 /** @hide */ 302 public static final int MDNS_DISCOVERY_MANAGER_EVENT = 23; 303 304 /** @hide */ 305 public static final int STOP_RESOLUTION = 24; 306 /** @hide */ 307 public static final int STOP_RESOLUTION_FAILED = 25; 308 /** @hide */ 309 public static final int STOP_RESOLUTION_SUCCEEDED = 26; 310 311 /** @hide */ 312 public static final int REGISTER_SERVICE_CALLBACK = 27; 313 /** @hide */ 314 public static final int REGISTER_SERVICE_CALLBACK_FAILED = 28; 315 /** @hide */ 316 public static final int SERVICE_UPDATED = 29; 317 /** @hide */ 318 public static final int SERVICE_UPDATED_LOST = 30; 319 320 /** @hide */ 321 public static final int UNREGISTER_SERVICE_CALLBACK = 31; 322 /** @hide */ 323 public static final int UNREGISTER_SERVICE_CALLBACK_SUCCEEDED = 32; 324 /** @hide */ 325 public static final int REGISTER_OFFLOAD_ENGINE = 33; 326 /** @hide */ 327 public static final int UNREGISTER_OFFLOAD_ENGINE = 34; 328 329 /** Dns based service discovery protocol */ 330 public static final int PROTOCOL_DNS_SD = 0x0001; 331 332 /** 333 * The minimum TTL seconds which is allowed for a service registration. 334 * 335 * @hide 336 */ 337 public static final long TTL_SECONDS_MIN = 30L; 338 339 /** 340 * The maximum TTL seconds which is allowed for a service registration. 341 * 342 * @hide 343 */ 344 public static final long TTL_SECONDS_MAX = 10 * 3600L; 345 346 private static final SparseArray<String> EVENT_NAMES = new SparseArray<>(); 347 static { EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES")348 EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES"); EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED")349 EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED"); EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED")350 EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED"); EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND")351 EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND"); EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST")352 EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST"); EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY")353 EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY"); EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED")354 EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED"); EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED")355 EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED"); EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE")356 EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE"); EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED")357 EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED"); EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED")358 EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED"); EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE")359 EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE"); EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED")360 EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED"); EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED")361 EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED"); EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE")362 EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE"); EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED")363 EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED"); EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED")364 EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED"); EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP")365 EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP"); EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP")366 EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP"); EVENT_NAMES.put(MDNS_SERVICE_EVENT, "MDNS_SERVICE_EVENT")367 EVENT_NAMES.put(MDNS_SERVICE_EVENT, "MDNS_SERVICE_EVENT"); EVENT_NAMES.put(STOP_RESOLUTION, "STOP_RESOLUTION")368 EVENT_NAMES.put(STOP_RESOLUTION, "STOP_RESOLUTION"); EVENT_NAMES.put(STOP_RESOLUTION_FAILED, "STOP_RESOLUTION_FAILED")369 EVENT_NAMES.put(STOP_RESOLUTION_FAILED, "STOP_RESOLUTION_FAILED"); EVENT_NAMES.put(STOP_RESOLUTION_SUCCEEDED, "STOP_RESOLUTION_SUCCEEDED")370 EVENT_NAMES.put(STOP_RESOLUTION_SUCCEEDED, "STOP_RESOLUTION_SUCCEEDED"); EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK, "REGISTER_SERVICE_CALLBACK")371 EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK, "REGISTER_SERVICE_CALLBACK"); EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK_FAILED, "REGISTER_SERVICE_CALLBACK_FAILED")372 EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK_FAILED, "REGISTER_SERVICE_CALLBACK_FAILED"); EVENT_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED")373 EVENT_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED"); EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK, "UNREGISTER_SERVICE_CALLBACK")374 EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK, "UNREGISTER_SERVICE_CALLBACK"); EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, "UNREGISTER_SERVICE_CALLBACK_SUCCEEDED")375 EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, 376 "UNREGISTER_SERVICE_CALLBACK_SUCCEEDED"); EVENT_NAMES.put(MDNS_DISCOVERY_MANAGER_EVENT, "MDNS_DISCOVERY_MANAGER_EVENT")377 EVENT_NAMES.put(MDNS_DISCOVERY_MANAGER_EVENT, "MDNS_DISCOVERY_MANAGER_EVENT"); EVENT_NAMES.put(REGISTER_CLIENT, "REGISTER_CLIENT")378 EVENT_NAMES.put(REGISTER_CLIENT, "REGISTER_CLIENT"); EVENT_NAMES.put(UNREGISTER_CLIENT, "UNREGISTER_CLIENT")379 EVENT_NAMES.put(UNREGISTER_CLIENT, "UNREGISTER_CLIENT"); 380 } 381 382 /** @hide */ nameOf(int event)383 public static String nameOf(int event) { 384 String name = EVENT_NAMES.get(event); 385 if (name == null) { 386 return Integer.toString(event); 387 } 388 return name; 389 } 390 391 private static final int FIRST_LISTENER_KEY = 1; 392 private static final int DNSSEC_PROTOCOL = 3; 393 394 private final INsdServiceConnector mService; 395 private final Context mContext; 396 397 private int mListenerKey = FIRST_LISTENER_KEY; 398 @GuardedBy("mMapLock") 399 private final SparseArray mListenerMap = new SparseArray(); 400 @GuardedBy("mMapLock") 401 private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); 402 @GuardedBy("mMapLock") 403 private final SparseArray<DiscoveryRequest> mDiscoveryMap = new SparseArray<>(); 404 @GuardedBy("mMapLock") 405 private final SparseArray<Executor> mExecutorMap = new SparseArray<>(); 406 private final Object mMapLock = new Object(); 407 // Map of listener key sent by client -> per-network discovery tracker 408 @GuardedBy("mPerNetworkDiscoveryMap") 409 private final ArrayMap<Integer, PerNetworkDiscoveryTracker> 410 mPerNetworkDiscoveryMap = new ArrayMap<>(); 411 412 @GuardedBy("mOffloadEngines") 413 private final ArrayList<OffloadEngineProxy> mOffloadEngines = new ArrayList<>(); 414 private final ServiceHandler mHandler; 415 416 private static class OffloadEngineProxy extends IOffloadEngine.Stub { 417 private final Executor mExecutor; 418 private final OffloadEngine mEngine; 419 OffloadEngineProxy(@onNull Executor executor, @NonNull OffloadEngine appCb)420 private OffloadEngineProxy(@NonNull Executor executor, @NonNull OffloadEngine appCb) { 421 mExecutor = executor; 422 mEngine = appCb; 423 } 424 425 @Override onOffloadServiceUpdated(OffloadServiceInfo info)426 public void onOffloadServiceUpdated(OffloadServiceInfo info) { 427 mExecutor.execute(() -> mEngine.onOffloadServiceUpdated(info)); 428 } 429 430 @Override onOffloadServiceRemoved(OffloadServiceInfo info)431 public void onOffloadServiceRemoved(OffloadServiceInfo info) { 432 mExecutor.execute(() -> mEngine.onOffloadServiceRemoved(info)); 433 } 434 } 435 436 /** 437 * Registers an OffloadEngine with NsdManager. 438 * 439 * A caller can register itself as an OffloadEngine if it supports mDns hardware offload. 440 * The caller must implement the {@link OffloadEngine} interface and update hardware offload 441 * state property when the {@link OffloadEngine#onOffloadServiceUpdated} and 442 * {@link OffloadEngine#onOffloadServiceRemoved} callback are called. Multiple engines may be 443 * registered for the same interface, and that the same engine cannot be registered twice. 444 * 445 * @param ifaceName indicates which network interface the hardware offload runs on 446 * @param offloadType the type of offload that the offload engine support 447 * @param offloadCapability the capabilities of the offload engine 448 * @param executor the executor on which to receive the offload callbacks 449 * @param engine the OffloadEngine that will receive the offload callbacks 450 * @throws IllegalStateException if the engine is already registered. 451 * 452 * @hide 453 */ 454 @FlaggedApi(NsdManager.Flags.REGISTER_NSD_OFFLOAD_ENGINE_API) 455 @SystemApi 456 @RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK, 457 NETWORK_STACK}) registerOffloadEngine(@onNull String ifaceName, @OffloadEngine.OffloadType long offloadType, @OffloadEngine.OffloadCapability long offloadCapability, @NonNull Executor executor, @NonNull OffloadEngine engine)458 public void registerOffloadEngine(@NonNull String ifaceName, 459 @OffloadEngine.OffloadType long offloadType, 460 @OffloadEngine.OffloadCapability long offloadCapability, @NonNull Executor executor, 461 @NonNull OffloadEngine engine) { 462 Objects.requireNonNull(ifaceName); 463 Objects.requireNonNull(executor); 464 Objects.requireNonNull(engine); 465 final OffloadEngineProxy cbImpl = new OffloadEngineProxy(executor, engine); 466 synchronized (mOffloadEngines) { 467 if (CollectionUtils.contains(mOffloadEngines, impl -> impl.mEngine == engine)) { 468 throw new IllegalStateException("This engine is already registered"); 469 } 470 mOffloadEngines.add(cbImpl); 471 } 472 try { 473 mService.registerOffloadEngine(ifaceName, cbImpl, offloadCapability, offloadType); 474 } catch (RemoteException e) { 475 e.rethrowFromSystemServer(); 476 } 477 } 478 479 480 /** 481 * Unregisters an OffloadEngine from NsdService. 482 * 483 * A caller can unregister itself as an OffloadEngine when it doesn't want to receive the 484 * callback anymore. The OffloadEngine must have been previously registered with the system 485 * using the {@link NsdManager#registerOffloadEngine} method. 486 * 487 * @param engine OffloadEngine object to be removed from NsdService 488 * @throws IllegalStateException if the engine is not registered. 489 * 490 * @hide 491 */ 492 @FlaggedApi(NsdManager.Flags.REGISTER_NSD_OFFLOAD_ENGINE_API) 493 @SystemApi 494 @RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK, 495 NETWORK_STACK}) unregisterOffloadEngine(@onNull OffloadEngine engine)496 public void unregisterOffloadEngine(@NonNull OffloadEngine engine) { 497 Objects.requireNonNull(engine); 498 final OffloadEngineProxy cbImpl; 499 synchronized (mOffloadEngines) { 500 final int index = CollectionUtils.indexOf(mOffloadEngines, 501 impl -> impl.mEngine == engine); 502 if (index < 0) { 503 throw new IllegalStateException("This engine is not registered"); 504 } 505 cbImpl = mOffloadEngines.remove(index); 506 } 507 508 try { 509 mService.unregisterOffloadEngine(cbImpl); 510 } catch (RemoteException e) { 511 e.rethrowFromSystemServer(); 512 } 513 } 514 515 private class PerNetworkDiscoveryTracker { 516 final String mServiceType; 517 final int mProtocolType; 518 final DiscoveryListener mBaseListener; 519 final Executor mBaseExecutor; 520 final ArrayMap<Network, DelegatingDiscoveryListener> mPerNetworkListeners = 521 new ArrayMap<>(); 522 523 final NetworkCallback mNetworkCb = new NetworkCallback() { 524 @Override 525 public void onAvailable(@NonNull Network network) { 526 final DelegatingDiscoveryListener wrappedListener = new DelegatingDiscoveryListener( 527 network, mBaseListener, mBaseExecutor); 528 mPerNetworkListeners.put(network, wrappedListener); 529 // Run discovery callbacks inline on the service handler thread, which is the 530 // same thread used by this NetworkCallback, but DelegatingDiscoveryListener will 531 // use the base executor to run the wrapped callbacks. 532 discoverServices(mServiceType, mProtocolType, network, Runnable::run, 533 wrappedListener); 534 } 535 536 @Override 537 public void onLost(@NonNull Network network) { 538 final DelegatingDiscoveryListener listener = mPerNetworkListeners.get(network); 539 if (listener == null) return; 540 listener.notifyAllServicesLost(); 541 // Listener will be removed from map in discovery stopped callback 542 stopServiceDiscovery(listener); 543 } 544 }; 545 546 // Accessed from mHandler 547 private boolean mStopRequested; 548 start(@onNull NetworkRequest request)549 public void start(@NonNull NetworkRequest request) { 550 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 551 cm.registerNetworkCallback(request, mNetworkCb, mHandler); 552 mHandler.post(() -> mBaseExecutor.execute(() -> 553 mBaseListener.onDiscoveryStarted(mServiceType))); 554 } 555 556 /** 557 * Stop discovery on all networks tracked by this class. 558 * 559 * This will request all underlying listeners to stop, and the last one to stop will call 560 * onDiscoveryStopped or onStopDiscoveryFailed. 561 * 562 * Must be called on the handler thread. 563 */ requestStop()564 public void requestStop() { 565 mHandler.post(() -> { 566 mStopRequested = true; 567 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 568 cm.unregisterNetworkCallback(mNetworkCb); 569 if (mPerNetworkListeners.size() == 0) { 570 mBaseExecutor.execute(() -> mBaseListener.onDiscoveryStopped(mServiceType)); 571 return; 572 } 573 for (int i = 0; i < mPerNetworkListeners.size(); i++) { 574 final DelegatingDiscoveryListener listener = mPerNetworkListeners.valueAt(i); 575 stopServiceDiscovery(listener); 576 } 577 }); 578 } 579 PerNetworkDiscoveryTracker(String serviceType, int protocolType, Executor baseExecutor, DiscoveryListener baseListener)580 private PerNetworkDiscoveryTracker(String serviceType, int protocolType, 581 Executor baseExecutor, DiscoveryListener baseListener) { 582 mServiceType = serviceType; 583 mProtocolType = protocolType; 584 mBaseExecutor = baseExecutor; 585 mBaseListener = baseListener; 586 } 587 588 /** 589 * Subset of NsdServiceInfo that is tracked to generate service lost notifications when a 590 * network is lost. 591 * 592 * Service lost notifications only contain service name, type and network, so only track 593 * that information (Network is known from the listener). This also implements 594 * equals/hashCode for usage in maps. 595 */ 596 private class TrackedNsdInfo { 597 private final String mServiceName; 598 private final String mServiceType; TrackedNsdInfo(NsdServiceInfo info)599 TrackedNsdInfo(NsdServiceInfo info) { 600 mServiceName = info.getServiceName(); 601 mServiceType = info.getServiceType(); 602 } 603 604 @Override hashCode()605 public int hashCode() { 606 return Objects.hash(mServiceName, mServiceType); 607 } 608 609 @Override equals(Object obj)610 public boolean equals(Object obj) { 611 if (!(obj instanceof TrackedNsdInfo)) return false; 612 final TrackedNsdInfo other = (TrackedNsdInfo) obj; 613 return Objects.equals(mServiceName, other.mServiceName) 614 && Objects.equals(mServiceType, other.mServiceType); 615 } 616 } 617 618 /** 619 * A listener wrapping calls to an app-provided listener, while keeping track of found 620 * services, so they can all be reported lost when the underlying network is lost. 621 * 622 * This should be registered to run on the service handler. 623 */ 624 private class DelegatingDiscoveryListener implements DiscoveryListener { 625 private final Network mNetwork; 626 private final DiscoveryListener mWrapped; 627 private final Executor mWrappedExecutor; 628 private final ArraySet<TrackedNsdInfo> mFoundInfo = new ArraySet<>(); 629 // When this flag is set to true, no further service found or lost callbacks should be 630 // handled. This flag indicates that the network for this DelegatingDiscoveryListener is 631 // lost, and any further callbacks would be redundant. 632 private boolean mAllServicesLost = false; 633 DelegatingDiscoveryListener(Network network, DiscoveryListener listener, Executor executor)634 private DelegatingDiscoveryListener(Network network, DiscoveryListener listener, 635 Executor executor) { 636 mNetwork = network; 637 mWrapped = listener; 638 mWrappedExecutor = executor; 639 } 640 notifyAllServicesLost()641 void notifyAllServicesLost() { 642 for (int i = 0; i < mFoundInfo.size(); i++) { 643 final TrackedNsdInfo trackedInfo = mFoundInfo.valueAt(i); 644 final NsdServiceInfo serviceInfo = new NsdServiceInfo( 645 trackedInfo.mServiceName, trackedInfo.mServiceType); 646 serviceInfo.setNetwork(mNetwork); 647 mWrappedExecutor.execute(() -> mWrapped.onServiceLost(serviceInfo)); 648 } 649 mAllServicesLost = true; 650 } 651 652 @Override onStartDiscoveryFailed(String serviceType, int errorCode)653 public void onStartDiscoveryFailed(String serviceType, int errorCode) { 654 // The delegated listener is used when NsdManager takes care of starting/stopping 655 // discovery on multiple networks. Failure to start on one network is not a global 656 // failure to be reported up, as other networks may succeed: just log. 657 Log.e(TAG, "Failed to start discovery for " + serviceType + " on " + mNetwork 658 + " with code " + errorCode); 659 mPerNetworkListeners.remove(mNetwork); 660 } 661 662 @Override onDiscoveryStarted(String serviceType)663 public void onDiscoveryStarted(String serviceType) { 664 // Wrapped listener was called upon registration, it is not called for discovery 665 // on each network 666 } 667 668 @Override onStopDiscoveryFailed(String serviceType, int errorCode)669 public void onStopDiscoveryFailed(String serviceType, int errorCode) { 670 Log.e(TAG, "Failed to stop discovery for " + serviceType + " on " + mNetwork 671 + " with code " + errorCode); 672 mPerNetworkListeners.remove(mNetwork); 673 if (mStopRequested && mPerNetworkListeners.size() == 0) { 674 // Do not report onStopDiscoveryFailed when some underlying listeners failed: 675 // this does not mean that all listeners did, and onStopDiscoveryFailed is not 676 // actionable anyway. Just report that discovery stopped. 677 mWrappedExecutor.execute(() -> mWrapped.onDiscoveryStopped(serviceType)); 678 } 679 } 680 681 @Override onDiscoveryStopped(String serviceType)682 public void onDiscoveryStopped(String serviceType) { 683 mPerNetworkListeners.remove(mNetwork); 684 if (mStopRequested && mPerNetworkListeners.size() == 0) { 685 mWrappedExecutor.execute(() -> mWrapped.onDiscoveryStopped(serviceType)); 686 } 687 } 688 689 @Override onServiceFound(NsdServiceInfo serviceInfo)690 public void onServiceFound(NsdServiceInfo serviceInfo) { 691 if (mAllServicesLost) { 692 // This DelegatingDiscoveryListener no longer has a network connection. Ignore 693 // the callback. 694 return; 695 } 696 mFoundInfo.add(new TrackedNsdInfo(serviceInfo)); 697 mWrappedExecutor.execute(() -> mWrapped.onServiceFound(serviceInfo)); 698 } 699 700 @Override onServiceLost(NsdServiceInfo serviceInfo)701 public void onServiceLost(NsdServiceInfo serviceInfo) { 702 if (mAllServicesLost) { 703 // This DelegatingDiscoveryListener no longer has a network connection. Ignore 704 // the callback. 705 return; 706 } 707 mFoundInfo.remove(new TrackedNsdInfo(serviceInfo)); 708 mWrappedExecutor.execute(() -> mWrapped.onServiceLost(serviceInfo)); 709 } 710 } 711 } 712 713 /** 714 * Create a new Nsd instance. Applications use 715 * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve 716 * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}. 717 * @param service the Binder interface 718 * @hide - hide this because it takes in a parameter of type INsdManager, which 719 * is a system private class. 720 */ NsdManager(Context context, INsdManager service)721 public NsdManager(Context context, INsdManager service) { 722 mContext = context; 723 // Use a common singleton thread ConnectivityThread to be shared among all nsd tasks. 724 // Instead of launching separate threads to handle tasks from the various instances. 725 mHandler = new ServiceHandler(ConnectivityThread.getInstanceLooper()); 726 727 try { 728 mService = service.connect(new NsdCallbackImpl(mHandler), CompatChanges.isChangeEnabled( 729 ENABLE_PLATFORM_MDNS_BACKEND)); 730 } catch (RemoteException e) { 731 throw new RuntimeException("Failed to connect to NsdService"); 732 } 733 734 // Only proactively start the daemon if the target SDK < S AND platform < V, For target 735 // SDK >= S AND platform < V, the internal service would automatically start/stop the native 736 // daemon as needed. For platform >= V, no action is required because the native daemon is 737 // completely removed. 738 if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) 739 && !SdkLevel.isAtLeastV()) { 740 try { 741 mService.startDaemon(); 742 } catch (RemoteException e) { 743 Log.e(TAG, "Failed to proactively start daemon"); 744 // Continue: the daemon can still be started on-demand later 745 } 746 } 747 } 748 749 private static class NsdCallbackImpl extends INsdManagerCallback.Stub { 750 private final Handler mServHandler; 751 NsdCallbackImpl(Handler serviceHandler)752 NsdCallbackImpl(Handler serviceHandler) { 753 mServHandler = serviceHandler; 754 } 755 sendInfo(int message, int listenerKey, NsdServiceInfo info)756 private void sendInfo(int message, int listenerKey, NsdServiceInfo info) { 757 mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info)); 758 } 759 sendDiscoveryRequest( int message, int listenerKey, DiscoveryRequest discoveryRequest)760 private void sendDiscoveryRequest( 761 int message, int listenerKey, DiscoveryRequest discoveryRequest) { 762 mServHandler.sendMessage( 763 mServHandler.obtainMessage(message, 0, listenerKey, discoveryRequest)); 764 } 765 sendError(int message, int listenerKey, int error)766 private void sendError(int message, int listenerKey, int error) { 767 mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey)); 768 } 769 sendNoArg(int message, int listenerKey)770 private void sendNoArg(int message, int listenerKey) { 771 mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey)); 772 } 773 774 @Override onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest)775 public void onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest) { 776 sendDiscoveryRequest(DISCOVER_SERVICES_STARTED, listenerKey, discoveryRequest); 777 } 778 779 @Override onDiscoverServicesFailed(int listenerKey, int error)780 public void onDiscoverServicesFailed(int listenerKey, int error) { 781 sendError(DISCOVER_SERVICES_FAILED, listenerKey, error); 782 } 783 784 @Override onServiceFound(int listenerKey, NsdServiceInfo info)785 public void onServiceFound(int listenerKey, NsdServiceInfo info) { 786 sendInfo(SERVICE_FOUND, listenerKey, info); 787 } 788 789 @Override onServiceLost(int listenerKey, NsdServiceInfo info)790 public void onServiceLost(int listenerKey, NsdServiceInfo info) { 791 sendInfo(SERVICE_LOST, listenerKey, info); 792 } 793 794 @Override onStopDiscoveryFailed(int listenerKey, int error)795 public void onStopDiscoveryFailed(int listenerKey, int error) { 796 sendError(STOP_DISCOVERY_FAILED, listenerKey, error); 797 } 798 799 @Override onStopDiscoverySucceeded(int listenerKey)800 public void onStopDiscoverySucceeded(int listenerKey) { 801 sendNoArg(STOP_DISCOVERY_SUCCEEDED, listenerKey); 802 } 803 804 @Override onRegisterServiceFailed(int listenerKey, int error)805 public void onRegisterServiceFailed(int listenerKey, int error) { 806 sendError(REGISTER_SERVICE_FAILED, listenerKey, error); 807 } 808 809 @Override onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info)810 public void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { 811 sendInfo(REGISTER_SERVICE_SUCCEEDED, listenerKey, info); 812 } 813 814 @Override onUnregisterServiceFailed(int listenerKey, int error)815 public void onUnregisterServiceFailed(int listenerKey, int error) { 816 sendError(UNREGISTER_SERVICE_FAILED, listenerKey, error); 817 } 818 819 @Override onUnregisterServiceSucceeded(int listenerKey)820 public void onUnregisterServiceSucceeded(int listenerKey) { 821 sendNoArg(UNREGISTER_SERVICE_SUCCEEDED, listenerKey); 822 } 823 824 @Override onResolveServiceFailed(int listenerKey, int error)825 public void onResolveServiceFailed(int listenerKey, int error) { 826 sendError(RESOLVE_SERVICE_FAILED, listenerKey, error); 827 } 828 829 @Override onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info)830 public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { 831 sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info); 832 } 833 834 @Override onStopResolutionFailed(int listenerKey, int error)835 public void onStopResolutionFailed(int listenerKey, int error) { 836 sendError(STOP_RESOLUTION_FAILED, listenerKey, error); 837 } 838 839 @Override onStopResolutionSucceeded(int listenerKey)840 public void onStopResolutionSucceeded(int listenerKey) { 841 sendNoArg(STOP_RESOLUTION_SUCCEEDED, listenerKey); 842 } 843 844 @Override onServiceInfoCallbackRegistrationFailed(int listenerKey, int error)845 public void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) { 846 sendError(REGISTER_SERVICE_CALLBACK_FAILED, listenerKey, error); 847 } 848 849 @Override onServiceUpdated(int listenerKey, NsdServiceInfo info)850 public void onServiceUpdated(int listenerKey, NsdServiceInfo info) { 851 sendInfo(SERVICE_UPDATED, listenerKey, info); 852 } 853 854 @Override onServiceUpdatedLost(int listenerKey)855 public void onServiceUpdatedLost(int listenerKey) { 856 sendNoArg(SERVICE_UPDATED_LOST, listenerKey); 857 } 858 859 @Override onServiceInfoCallbackUnregistered(int listenerKey)860 public void onServiceInfoCallbackUnregistered(int listenerKey) { 861 sendNoArg(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, listenerKey); 862 } 863 } 864 865 /** 866 * Failures are passed with {@link RegistrationListener#onRegistrationFailed}, 867 * {@link RegistrationListener#onUnregistrationFailed}, 868 * {@link DiscoveryListener#onStartDiscoveryFailed}, 869 * {@link DiscoveryListener#onStopDiscoveryFailed} or {@link ResolveListener#onResolveFailed}. 870 * 871 * Indicates that the operation failed due to an internal error. 872 */ 873 public static final int FAILURE_INTERNAL_ERROR = 0; 874 875 /** 876 * Indicates that the operation failed because it is already active. 877 */ 878 public static final int FAILURE_ALREADY_ACTIVE = 3; 879 880 /** 881 * Indicates that the operation failed because the maximum outstanding 882 * requests from the applications have reached. 883 */ 884 public static final int FAILURE_MAX_LIMIT = 4; 885 886 /** 887 * Indicates that the stop operation failed because it is not running. 888 * This failure is passed with {@link ResolveListener#onStopResolutionFailed}. 889 */ 890 public static final int FAILURE_OPERATION_NOT_RUNNING = 5; 891 892 /** 893 * Indicates that the service has failed to resolve because of bad parameters. 894 * 895 * This failure is passed with 896 * {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed}. 897 */ 898 public static final int FAILURE_BAD_PARAMETERS = 6; 899 900 /** @hide */ 901 @Retention(RetentionPolicy.SOURCE) 902 @IntDef(value = { 903 FAILURE_OPERATION_NOT_RUNNING, 904 }) 905 public @interface StopOperationFailureCode { 906 } 907 908 /** @hide */ 909 @Retention(RetentionPolicy.SOURCE) 910 @IntDef(value = { 911 FAILURE_ALREADY_ACTIVE, 912 FAILURE_BAD_PARAMETERS, 913 }) 914 public @interface ResolutionFailureCode { 915 } 916 917 /** Interface for callback invocation for service discovery */ 918 public interface DiscoveryListener { 919 onStartDiscoveryFailed(String serviceType, int errorCode)920 public void onStartDiscoveryFailed(String serviceType, int errorCode); 921 onStopDiscoveryFailed(String serviceType, int errorCode)922 public void onStopDiscoveryFailed(String serviceType, int errorCode); 923 onDiscoveryStarted(String serviceType)924 public void onDiscoveryStarted(String serviceType); 925 onDiscoveryStopped(String serviceType)926 public void onDiscoveryStopped(String serviceType); 927 onServiceFound(NsdServiceInfo serviceInfo)928 public void onServiceFound(NsdServiceInfo serviceInfo); 929 onServiceLost(NsdServiceInfo serviceInfo)930 public void onServiceLost(NsdServiceInfo serviceInfo); 931 } 932 933 /** Interface for callback invocation for service registration */ 934 public interface RegistrationListener { 935 onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode)936 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 937 onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode)938 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode); 939 onServiceRegistered(NsdServiceInfo serviceInfo)940 public void onServiceRegistered(NsdServiceInfo serviceInfo); 941 onServiceUnregistered(NsdServiceInfo serviceInfo)942 public void onServiceUnregistered(NsdServiceInfo serviceInfo); 943 } 944 945 /** 946 * Callback for use with {@link NsdManager#resolveService} to resolve the service info and use 947 * with {@link NsdManager#stopServiceResolution} to stop resolution. 948 */ 949 public interface ResolveListener { 950 951 /** 952 * Called on the internal thread or with an executor passed to 953 * {@link NsdManager#resolveService} to report the resolution was failed with an error. 954 * 955 * A resolution operation would call either onServiceResolved or onResolveFailed once based 956 * on the result. 957 */ onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)958 void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode); 959 960 /** 961 * Called on the internal thread or with an executor passed to 962 * {@link NsdManager#resolveService} to report the resolved service info. 963 * 964 * A resolution operation would call either onServiceResolved or onResolveFailed once based 965 * on the result. 966 */ onServiceResolved(NsdServiceInfo serviceInfo)967 void onServiceResolved(NsdServiceInfo serviceInfo); 968 969 /** 970 * Called on the internal thread or with an executor passed to 971 * {@link NsdManager#resolveService} to report the resolution was stopped. 972 * 973 * A stop resolution operation would call either onResolutionStopped or 974 * onStopResolutionFailed once based on the result. 975 */ onResolutionStopped(@onNull NsdServiceInfo serviceInfo)976 default void onResolutionStopped(@NonNull NsdServiceInfo serviceInfo) { } 977 978 /** 979 * Called once on the internal thread or with an executor passed to 980 * {@link NsdManager#resolveService} to report that stopping resolution failed with an 981 * error. 982 * 983 * A stop resolution operation would call either onResolutionStopped or 984 * onStopResolutionFailed once based on the result. 985 */ onStopResolutionFailed(@onNull NsdServiceInfo serviceInfo, @StopOperationFailureCode int errorCode)986 default void onStopResolutionFailed(@NonNull NsdServiceInfo serviceInfo, 987 @StopOperationFailureCode int errorCode) { } 988 } 989 990 /** 991 * Callback to listen to service info updates. 992 * 993 * For use with {@link NsdManager#registerServiceInfoCallback} to register, and with 994 * {@link NsdManager#unregisterServiceInfoCallback} to stop listening. 995 */ 996 public interface ServiceInfoCallback { 997 998 /** 999 * Reports that registering the callback failed with an error. 1000 * 1001 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. 1002 * 1003 * onServiceInfoCallbackRegistrationFailed will be called exactly once when the callback 1004 * could not be registered. No other callback will be sent in that case. 1005 */ onServiceInfoCallbackRegistrationFailed(@esolutionFailureCode int errorCode)1006 void onServiceInfoCallbackRegistrationFailed(@ResolutionFailureCode int errorCode); 1007 1008 /** 1009 * Reports updated service info. 1010 * 1011 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. Any 1012 * service updates will be notified via this callback until 1013 * {@link NsdManager#unregisterServiceInfoCallback} is called. This will only be called once 1014 * the service is found, so may never be called if the service is never present. 1015 */ onServiceUpdated(@onNull NsdServiceInfo serviceInfo)1016 void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo); 1017 1018 /** 1019 * Reports when the service that this callback listens to becomes unavailable. 1020 * 1021 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. The 1022 * service may become available again, in which case {@link #onServiceUpdated} will be 1023 * called. 1024 */ onServiceLost()1025 void onServiceLost(); 1026 1027 /** 1028 * Reports that service info updates have stopped. 1029 * 1030 * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. 1031 * 1032 * A callback unregistration operation will call onServiceInfoCallbackUnregistered 1033 * once. After this, the callback may be reused. 1034 */ onServiceInfoCallbackUnregistered()1035 void onServiceInfoCallbackUnregistered(); 1036 } 1037 1038 @VisibleForTesting 1039 class ServiceHandler extends Handler { ServiceHandler(Looper looper)1040 ServiceHandler(Looper looper) { 1041 super(looper); 1042 } 1043 1044 @Override handleMessage(Message message)1045 public void handleMessage(Message message) { 1046 // Do not use message in the executor lambdas, as it will be recycled once this method 1047 // returns. Keep references to its content instead. 1048 final int what = message.what; 1049 final int errorCode = message.arg1; 1050 final int key = message.arg2; 1051 final Object obj = message.obj; 1052 final Object listener; 1053 final NsdServiceInfo ns; 1054 final DiscoveryRequest discoveryRequest; 1055 final Executor executor; 1056 synchronized (mMapLock) { 1057 listener = mListenerMap.get(key); 1058 ns = mServiceMap.get(key); 1059 discoveryRequest = mDiscoveryMap.get(key); 1060 executor = mExecutorMap.get(key); 1061 } 1062 if (listener == null) { 1063 Log.d(TAG, "Stale key " + key); 1064 return; 1065 } 1066 if (DBG) { 1067 if (discoveryRequest != null) { 1068 Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", discovery " 1069 + discoveryRequest); 1070 } else { 1071 Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns); 1072 } 1073 } 1074 switch (what) { 1075 case DISCOVER_SERVICES_STARTED: 1076 final String s = getNsdServiceInfoType((DiscoveryRequest) obj); 1077 executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStarted(s)); 1078 break; 1079 case DISCOVER_SERVICES_FAILED: 1080 removeListener(key); 1081 executor.execute(() -> ((DiscoveryListener) listener).onStartDiscoveryFailed( 1082 getNsdServiceInfoType(discoveryRequest), errorCode)); 1083 break; 1084 case SERVICE_FOUND: 1085 executor.execute(() -> ((DiscoveryListener) listener).onServiceFound( 1086 (NsdServiceInfo) obj)); 1087 break; 1088 case SERVICE_LOST: 1089 executor.execute(() -> ((DiscoveryListener) listener).onServiceLost( 1090 (NsdServiceInfo) obj)); 1091 break; 1092 case STOP_DISCOVERY_FAILED: 1093 // TODO: failure to stop discovery should be internal and retried internally, as 1094 // the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED 1095 removeListener(key); 1096 executor.execute(() -> ((DiscoveryListener) listener).onStopDiscoveryFailed( 1097 getNsdServiceInfoType(discoveryRequest), errorCode)); 1098 break; 1099 case STOP_DISCOVERY_SUCCEEDED: 1100 removeListener(key); 1101 executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStopped( 1102 getNsdServiceInfoType(discoveryRequest))); 1103 break; 1104 case REGISTER_SERVICE_FAILED: 1105 removeListener(key); 1106 executor.execute(() -> ((RegistrationListener) listener).onRegistrationFailed( 1107 ns, errorCode)); 1108 break; 1109 case REGISTER_SERVICE_SUCCEEDED: 1110 executor.execute(() -> ((RegistrationListener) listener).onServiceRegistered( 1111 (NsdServiceInfo) obj)); 1112 break; 1113 case UNREGISTER_SERVICE_FAILED: 1114 removeListener(key); 1115 executor.execute(() -> ((RegistrationListener) listener).onUnregistrationFailed( 1116 ns, errorCode)); 1117 break; 1118 case UNREGISTER_SERVICE_SUCCEEDED: 1119 // TODO: do not unregister listener until service is unregistered, or provide 1120 // alternative way for unregistering ? 1121 removeListener(key); 1122 executor.execute(() -> ((RegistrationListener) listener).onServiceUnregistered( 1123 ns)); 1124 break; 1125 case RESOLVE_SERVICE_FAILED: 1126 removeListener(key); 1127 executor.execute(() -> ((ResolveListener) listener).onResolveFailed( 1128 ns, errorCode)); 1129 break; 1130 case RESOLVE_SERVICE_SUCCEEDED: 1131 removeListener(key); 1132 executor.execute(() -> ((ResolveListener) listener).onServiceResolved( 1133 (NsdServiceInfo) obj)); 1134 break; 1135 case STOP_RESOLUTION_FAILED: 1136 removeListener(key); 1137 executor.execute(() -> ((ResolveListener) listener).onStopResolutionFailed( 1138 ns, errorCode)); 1139 break; 1140 case STOP_RESOLUTION_SUCCEEDED: 1141 removeListener(key); 1142 executor.execute(() -> ((ResolveListener) listener).onResolutionStopped( 1143 ns)); 1144 break; 1145 case REGISTER_SERVICE_CALLBACK_FAILED: 1146 removeListener(key); 1147 executor.execute(() -> ((ServiceInfoCallback) listener) 1148 .onServiceInfoCallbackRegistrationFailed(errorCode)); 1149 break; 1150 case SERVICE_UPDATED: 1151 executor.execute(() -> ((ServiceInfoCallback) listener) 1152 .onServiceUpdated((NsdServiceInfo) obj)); 1153 break; 1154 case SERVICE_UPDATED_LOST: 1155 executor.execute(() -> ((ServiceInfoCallback) listener).onServiceLost()); 1156 break; 1157 case UNREGISTER_SERVICE_CALLBACK_SUCCEEDED: 1158 removeListener(key); 1159 executor.execute(() -> ((ServiceInfoCallback) listener) 1160 .onServiceInfoCallbackUnregistered()); 1161 break; 1162 default: 1163 Log.d(TAG, "Ignored " + message); 1164 break; 1165 } 1166 } 1167 } 1168 nextListenerKey()1169 private int nextListenerKey() { 1170 // Ensure mListenerKey >= FIRST_LISTENER_KEY; 1171 mListenerKey = Math.max(FIRST_LISTENER_KEY, mListenerKey + 1); 1172 return mListenerKey; 1173 } 1174 putListener(Object listener, Executor e, NsdServiceInfo serviceInfo)1175 private int putListener(Object listener, Executor e, NsdServiceInfo serviceInfo) { 1176 synchronized (mMapLock) { 1177 return putListener(listener, e, mServiceMap, serviceInfo); 1178 } 1179 } 1180 putListener(Object listener, Executor e, DiscoveryRequest discoveryRequest)1181 private int putListener(Object listener, Executor e, DiscoveryRequest discoveryRequest) { 1182 synchronized (mMapLock) { 1183 return putListener(listener, e, mDiscoveryMap, discoveryRequest); 1184 } 1185 } 1186 1187 // Assert that the listener is not in the map, then add it and returns its key putListener(Object listener, Executor e, SparseArray<T> map, T value)1188 private <T> int putListener(Object listener, Executor e, SparseArray<T> map, T value) { 1189 synchronized (mMapLock) { 1190 checkListener(listener); 1191 final int key; 1192 final int valueIndex = mListenerMap.indexOfValue(listener); 1193 if (valueIndex != -1) { 1194 throw new IllegalArgumentException("listener already in use"); 1195 } 1196 key = nextListenerKey(); 1197 mListenerMap.put(key, listener); 1198 map.put(key, value); 1199 mExecutorMap.put(key, e); 1200 return key; 1201 } 1202 } 1203 updateRegisteredListener(Object listener, Executor e, NsdServiceInfo s)1204 private int updateRegisteredListener(Object listener, Executor e, NsdServiceInfo s) { 1205 final int key; 1206 synchronized (mMapLock) { 1207 key = getListenerKey(listener); 1208 mServiceMap.put(key, s); 1209 mExecutorMap.put(key, e); 1210 } 1211 return key; 1212 } 1213 removeListener(int key)1214 private void removeListener(int key) { 1215 synchronized (mMapLock) { 1216 mListenerMap.remove(key); 1217 mServiceMap.remove(key); 1218 mDiscoveryMap.remove(key); 1219 mExecutorMap.remove(key); 1220 } 1221 } 1222 getListenerKey(Object listener)1223 private int getListenerKey(Object listener) { 1224 checkListener(listener); 1225 synchronized (mMapLock) { 1226 int valueIndex = mListenerMap.indexOfValue(listener); 1227 if (valueIndex == -1) { 1228 throw new IllegalArgumentException("listener not registered"); 1229 } 1230 return mListenerMap.keyAt(valueIndex); 1231 } 1232 } 1233 getNsdServiceInfoType(DiscoveryRequest r)1234 private static String getNsdServiceInfoType(DiscoveryRequest r) { 1235 if (r == null) return "?"; 1236 return r.getServiceType(); 1237 } 1238 1239 /** 1240 * Register a service to be discovered by other services. 1241 * 1242 * <p> The function call immediately returns after sending a request to register service 1243 * to the framework. The application is notified of a successful registration 1244 * through the callback {@link RegistrationListener#onServiceRegistered} or a failure 1245 * through {@link RegistrationListener#onRegistrationFailed}. 1246 * 1247 * <p> The application should call {@link #unregisterService} when the service 1248 * registration is no longer required, and/or whenever the application is stopped. 1249 * 1250 * @param serviceInfo The service being registered 1251 * @param protocolType The service discovery protocol 1252 * @param listener The listener notifies of a successful registration and is used to 1253 * unregister this service through a call on {@link #unregisterService}. Cannot be null. 1254 * Cannot be in use for an active service registration. 1255 */ registerService(NsdServiceInfo serviceInfo, int protocolType, RegistrationListener listener)1256 public void registerService(NsdServiceInfo serviceInfo, int protocolType, 1257 RegistrationListener listener) { 1258 registerService(serviceInfo, protocolType, Runnable::run, listener); 1259 } 1260 1261 /** 1262 * Register a service to be discovered by other services. 1263 * 1264 * <p> The function call immediately returns after sending a request to register service 1265 * to the framework. The application is notified of a successful registration 1266 * through the callback {@link RegistrationListener#onServiceRegistered} or a failure 1267 * through {@link RegistrationListener#onRegistrationFailed}. 1268 * 1269 * <p> The application should call {@link #unregisterService} when the service 1270 * registration is no longer required, and/or whenever the application is stopped. 1271 * @param serviceInfo The service being registered 1272 * @param protocolType The service discovery protocol 1273 * @param executor Executor to run listener callbacks with 1274 * @param listener The listener notifies of a successful registration and is used to 1275 * unregister this service through a call on {@link #unregisterService}. Cannot be null. 1276 */ registerService(@onNull NsdServiceInfo serviceInfo, int protocolType, @NonNull Executor executor, @NonNull RegistrationListener listener)1277 public void registerService(@NonNull NsdServiceInfo serviceInfo, int protocolType, 1278 @NonNull Executor executor, @NonNull RegistrationListener listener) { 1279 checkServiceInfoForRegistration(serviceInfo); 1280 checkProtocol(protocolType); 1281 final AdvertisingRequest.Builder builder = new AdvertisingRequest.Builder(serviceInfo, 1282 protocolType); 1283 // Optionally assume that the request is an update request if it uses subtypes and the same 1284 // listener. This is not documented behavior as support for advertising subtypes via 1285 // "_servicename,_sub1,_sub2" has never been documented in the first place, and using 1286 // multiple subtypes was broken in T until a later module update. Subtype registration is 1287 // documented in the NsdServiceInfo.setSubtypes API instead, but this provides a limited 1288 // option for users of the older undocumented behavior, only for subtype changes. 1289 if (isSubtypeUpdateRequest(serviceInfo, listener)) { 1290 builder.setAdvertisingConfig(AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY); 1291 } 1292 registerService(builder.build(), executor, listener); 1293 } 1294 isSubtypeUpdateRequest(@onNull NsdServiceInfo serviceInfo, @NonNull RegistrationListener listener)1295 private boolean isSubtypeUpdateRequest(@NonNull NsdServiceInfo serviceInfo, @NonNull 1296 RegistrationListener listener) { 1297 // If the listener is the same object, serviceInfo is for the same service name and 1298 // type (outside of subtypes), and either of them use subtypes, treat the request as a 1299 // subtype update request. 1300 synchronized (mMapLock) { 1301 int valueIndex = mListenerMap.indexOfValue(listener); 1302 if (valueIndex == -1) { 1303 return false; 1304 } 1305 final int key = mListenerMap.keyAt(valueIndex); 1306 NsdServiceInfo existingService = mServiceMap.get(key); 1307 if (existingService == null) { 1308 return false; 1309 } 1310 final Pair<String, String> existingTypeSubtype = getTypeAndSubtypes( 1311 existingService.getServiceType()); 1312 final Pair<String, String> newTypeSubtype = getTypeAndSubtypes( 1313 serviceInfo.getServiceType()); 1314 if (existingTypeSubtype == null || newTypeSubtype == null) { 1315 return false; 1316 } 1317 final boolean existingHasNoSubtype = TextUtils.isEmpty(existingTypeSubtype.second); 1318 final boolean updatedHasNoSubtype = TextUtils.isEmpty(newTypeSubtype.second); 1319 if (existingHasNoSubtype && updatedHasNoSubtype) { 1320 // Only allow subtype changes when subtypes are used. This ensures that this 1321 // behavior does not affect most requests. 1322 return false; 1323 } 1324 1325 return Objects.equals(existingService.getServiceName(), serviceInfo.getServiceName()) 1326 && Objects.equals(existingTypeSubtype.first, newTypeSubtype.first); 1327 } 1328 } 1329 1330 /** 1331 * Get the base type from a type specification with "_type._tcp,sub1,sub2" syntax. 1332 * 1333 * <p>This rejects specifications using dot syntax to specify subtypes ("_sub1._type._tcp"). 1334 * 1335 * @return Type and comma-separated list of subtypes, or null if invalid format. 1336 */ 1337 @Nullable getTypeAndSubtypes(@ullable String typeWithSubtype)1338 private static Pair<String, String> getTypeAndSubtypes(@Nullable String typeWithSubtype) { 1339 if (typeWithSubtype == null) { 1340 return null; 1341 } 1342 final Matcher matcher = Pattern.compile(TYPE_REGEX).matcher(typeWithSubtype); 1343 if (!matcher.matches()) return null; 1344 // Reject specifications using leading subtypes with a dot 1345 if (!TextUtils.isEmpty(matcher.group(1))) return null; 1346 return new Pair<>(matcher.group(2), matcher.group(3)); 1347 } 1348 1349 /** 1350 * Register a service to be discovered by other services. 1351 * 1352 * <p> The function call immediately returns after sending a request to register service 1353 * to the framework. The application is notified of a successful registration 1354 * through the callback {@link RegistrationListener#onServiceRegistered} or a failure 1355 * through {@link RegistrationListener#onRegistrationFailed}. 1356 * 1357 * <p> The application should call {@link #unregisterService} when the service 1358 * registration is no longer required, and/or whenever the application is stopped. 1359 * @param advertisingRequest service being registered 1360 * @param executor Executor to run listener callbacks with 1361 * @param listener The listener notifies of a successful registration and is used to 1362 * unregister this service through a call on {@link #unregisterService}. Cannot be null. 1363 * 1364 * @hide 1365 */ 1366 // @FlaggedApi(Flags.ADVERTISE_REQUEST_API) registerService(@onNull AdvertisingRequest advertisingRequest, @NonNull Executor executor, @NonNull RegistrationListener listener)1367 public void registerService(@NonNull AdvertisingRequest advertisingRequest, 1368 @NonNull Executor executor, 1369 @NonNull RegistrationListener listener) { 1370 final NsdServiceInfo serviceInfo = advertisingRequest.getServiceInfo(); 1371 final int protocolType = advertisingRequest.getProtocolType(); 1372 checkServiceInfoForRegistration(serviceInfo); 1373 checkProtocol(protocolType); 1374 final int key; 1375 // For update only request, the old listener has to be reused 1376 if ((advertisingRequest.getAdvertisingConfig() 1377 & AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY) > 0) { 1378 key = updateRegisteredListener(listener, executor, serviceInfo); 1379 } else { 1380 key = putListener(listener, executor, serviceInfo); 1381 } 1382 try { 1383 mService.registerService(key, advertisingRequest); 1384 } catch (RemoteException e) { 1385 e.rethrowFromSystemServer(); 1386 } 1387 } 1388 1389 /** 1390 * Unregister a service registered through {@link #registerService}. A successful 1391 * unregister is notified to the application with a call to 1392 * {@link RegistrationListener#onServiceUnregistered}. 1393 * 1394 * @param listener This should be the listener object that was passed to 1395 * {@link #registerService}. It identifies the service that should be unregistered 1396 * and notifies of a successful or unsuccessful unregistration via the listener 1397 * callbacks. In API versions 20 and above, the listener object may be used for 1398 * another service registration once the callback has been called. In API versions <= 19, 1399 * there is no entirely reliable way to know when a listener may be re-used, and a new 1400 * listener should be created for each service registration request. 1401 */ unregisterService(RegistrationListener listener)1402 public void unregisterService(RegistrationListener listener) { 1403 int id = getListenerKey(listener); 1404 try { 1405 mService.unregisterService(id); 1406 } catch (RemoteException e) { 1407 e.rethrowFromSystemServer(); 1408 } 1409 } 1410 1411 /** 1412 * Initiate service discovery to browse for instances of a service type. Service discovery 1413 * consumes network bandwidth and will continue until the application calls 1414 * {@link #stopServiceDiscovery}. 1415 * 1416 * <p> The function call immediately returns after sending a request to start service 1417 * discovery to the framework. The application is notified of a success to initiate 1418 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 1419 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 1420 * 1421 * <p> Upon successful start, application is notified when a service is found with 1422 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 1423 * {@link DiscoveryListener#onServiceLost}. 1424 * 1425 * <p> Upon failure to start, service discovery is not active and application does 1426 * not need to invoke {@link #stopServiceDiscovery} 1427 * 1428 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 1429 * service type is no longer required, and/or whenever the application is paused or 1430 * stopped. 1431 * 1432 * @param serviceType The service type being discovered. Examples include "_http._tcp" for 1433 * http services or "_ipp._tcp" for printers 1434 * @param protocolType The service discovery protocol 1435 * @param listener The listener notifies of a successful discovery and is used 1436 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 1437 * Cannot be null. Cannot be in use for an active service discovery. 1438 */ discoverServices(String serviceType, int protocolType, DiscoveryListener listener)1439 public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { 1440 discoverServices(serviceType, protocolType, (Network) null, Runnable::run, listener); 1441 } 1442 1443 /** 1444 * Initiate service discovery to browse for instances of a service type. Service discovery 1445 * consumes network bandwidth and will continue until the application calls 1446 * {@link #stopServiceDiscovery}. 1447 * 1448 * <p> The function call immediately returns after sending a request to start service 1449 * discovery to the framework. The application is notified of a success to initiate 1450 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 1451 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 1452 * 1453 * <p> Upon successful start, application is notified when a service is found with 1454 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 1455 * {@link DiscoveryListener#onServiceLost}. 1456 * 1457 * <p> Upon failure to start, service discovery is not active and application does 1458 * not need to invoke {@link #stopServiceDiscovery} 1459 * 1460 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 1461 * service type is no longer required, and/or whenever the application is paused or 1462 * stopped. 1463 * @param serviceType The service type being discovered. Examples include "_http._tcp" for 1464 * http services or "_ipp._tcp" for printers 1465 * @param protocolType The service discovery protocol 1466 * @param network Network to discover services on, or null to discover on all available networks 1467 * @param executor Executor to run listener callbacks with 1468 * @param listener The listener notifies of a successful discovery and is used 1469 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 1470 */ discoverServices(@onNull String serviceType, int protocolType, @Nullable Network network, @NonNull Executor executor, @NonNull DiscoveryListener listener)1471 public void discoverServices(@NonNull String serviceType, int protocolType, 1472 @Nullable Network network, @NonNull Executor executor, 1473 @NonNull DiscoveryListener listener) { 1474 if (TextUtils.isEmpty(serviceType)) { 1475 throw new IllegalArgumentException("Service type cannot be empty"); 1476 } 1477 DiscoveryRequest request = new DiscoveryRequest.Builder(protocolType, serviceType) 1478 .setNetwork(network).build(); 1479 discoverServices(request, executor, listener); 1480 } 1481 1482 /** 1483 * Initiates service discovery to browse for instances of a service type. Service discovery 1484 * consumes network bandwidth and will continue until the application calls 1485 * {@link #stopServiceDiscovery}. 1486 * 1487 * <p> The function call immediately returns after sending a request to start service 1488 * discovery to the framework. The application is notified of a success to initiate 1489 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 1490 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 1491 * 1492 * <p> Upon successful start, application is notified when a service is found with 1493 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 1494 * {@link DiscoveryListener#onServiceLost}. 1495 * 1496 * <p> Upon failure to start, service discovery is not active and application does 1497 * not need to invoke {@link #stopServiceDiscovery} 1498 * 1499 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 1500 * service type is no longer required, and/or whenever the application is paused or 1501 * stopped. 1502 * 1503 * @param discoveryRequest the {@link DiscoveryRequest} object which specifies the discovery 1504 * parameters such as service type, subtype and network 1505 * @param executor Executor to run listener callbacks with 1506 * @param listener The listener notifies of a successful discovery and is used 1507 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 1508 */ 1509 @FlaggedApi(Flags.NSD_SUBTYPES_SUPPORT_ENABLED) discoverServices(@onNull DiscoveryRequest discoveryRequest, @NonNull Executor executor, @NonNull DiscoveryListener listener)1510 public void discoverServices(@NonNull DiscoveryRequest discoveryRequest, 1511 @NonNull Executor executor, @NonNull DiscoveryListener listener) { 1512 int key = putListener(listener, executor, discoveryRequest); 1513 try { 1514 mService.discoverServices(key, discoveryRequest); 1515 } catch (RemoteException e) { 1516 e.rethrowFromSystemServer(); 1517 } 1518 } 1519 1520 /** 1521 * Initiate service discovery to browse for instances of a service type. Service discovery 1522 * consumes network bandwidth and will continue until the application calls 1523 * {@link #stopServiceDiscovery}. 1524 * 1525 * <p> The function call immediately returns after sending a request to start service 1526 * discovery to the framework. The application is notified of a success to initiate 1527 * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure 1528 * through {@link DiscoveryListener#onStartDiscoveryFailed}. 1529 * 1530 * <p> Upon successful start, application is notified when a service is found with 1531 * {@link DiscoveryListener#onServiceFound} or when a service is lost with 1532 * {@link DiscoveryListener#onServiceLost}. 1533 * 1534 * <p> Upon failure to start, service discovery is not active and application does 1535 * not need to invoke {@link #stopServiceDiscovery} 1536 * 1537 * <p> The application should call {@link #stopServiceDiscovery} when discovery of this 1538 * service type is no longer required, and/or whenever the application is paused or 1539 * stopped. 1540 * 1541 * <p> During discovery, new networks may connect or existing networks may disconnect - for 1542 * example if wifi is reconnected. When a service was found on a network that disconnects, 1543 * {@link DiscoveryListener#onServiceLost} will be called. If a new network connects that 1544 * matches the {@link NetworkRequest}, {@link DiscoveryListener#onServiceFound} will be called 1545 * for services found on that network. Applications that do not want to track networks 1546 * themselves are encouraged to use this method instead of other overloads of 1547 * {@code discoverServices}, as they will receive proper notifications when a service becomes 1548 * available or unavailable due to network changes. 1549 * @param serviceType The service type being discovered. Examples include "_http._tcp" for 1550 * http services or "_ipp._tcp" for printers 1551 * @param protocolType The service discovery protocol 1552 * @param networkRequest Request specifying networks that should be considered when discovering 1553 * @param executor Executor to run listener callbacks with 1554 * @param listener The listener notifies of a successful discovery and is used 1555 * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. 1556 */ 1557 @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) discoverServices(@onNull String serviceType, int protocolType, @NonNull NetworkRequest networkRequest, @NonNull Executor executor, @NonNull DiscoveryListener listener)1558 public void discoverServices(@NonNull String serviceType, int protocolType, 1559 @NonNull NetworkRequest networkRequest, @NonNull Executor executor, 1560 @NonNull DiscoveryListener listener) { 1561 if (TextUtils.isEmpty(serviceType)) { 1562 throw new IllegalArgumentException("Service type cannot be empty"); 1563 } 1564 Objects.requireNonNull(networkRequest, "NetworkRequest cannot be null"); 1565 DiscoveryRequest discoveryRequest = 1566 new DiscoveryRequest.Builder(protocolType, serviceType).build(); 1567 1568 final int baseListenerKey = putListener(listener, executor, discoveryRequest); 1569 1570 final PerNetworkDiscoveryTracker discoveryInfo = new PerNetworkDiscoveryTracker( 1571 serviceType, protocolType, executor, listener); 1572 1573 synchronized (mPerNetworkDiscoveryMap) { 1574 mPerNetworkDiscoveryMap.put(baseListenerKey, discoveryInfo); 1575 discoveryInfo.start(networkRequest); 1576 } 1577 } 1578 1579 /** 1580 * Stop service discovery initiated with {@link #discoverServices}. An active service 1581 * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted} 1582 * and it stays active until the application invokes a stop service discovery. A successful 1583 * stop is notified to with a call to {@link DiscoveryListener#onDiscoveryStopped}. 1584 * 1585 * <p> Upon failure to stop service discovery, application is notified through 1586 * {@link DiscoveryListener#onStopDiscoveryFailed}. 1587 * 1588 * @param listener This should be the listener object that was passed to {@link #discoverServices}. 1589 * It identifies the discovery that should be stopped and notifies of a successful or 1590 * unsuccessful stop. In API versions 20 and above, the listener object may be used for 1591 * another service discovery once the callback has been called. In API versions <= 19, 1592 * there is no entirely reliable way to know when a listener may be re-used, and a new 1593 * listener should be created for each service discovery request. 1594 */ stopServiceDiscovery(DiscoveryListener listener)1595 public void stopServiceDiscovery(DiscoveryListener listener) { 1596 int id = getListenerKey(listener); 1597 // If this is a PerNetworkDiscovery request, handle it as such 1598 synchronized (mPerNetworkDiscoveryMap) { 1599 final PerNetworkDiscoveryTracker info = mPerNetworkDiscoveryMap.get(id); 1600 if (info != null) { 1601 info.requestStop(); 1602 return; 1603 } 1604 } 1605 try { 1606 mService.stopDiscovery(id); 1607 } catch (RemoteException e) { 1608 e.rethrowFromSystemServer(); 1609 } 1610 } 1611 1612 /** 1613 * Resolve a discovered service. An application can resolve a service right before 1614 * establishing a connection to fetch the IP and port details on which to setup 1615 * the connection. 1616 * 1617 * @param serviceInfo service to be resolved 1618 * @param listener to receive callback upon success or failure. Cannot be null. 1619 * Cannot be in use for an active service resolution. 1620 * 1621 * @deprecated the returned ServiceInfo may get stale at any time after resolution, including 1622 * immediately after the callback is called, and may not contain some service information that 1623 * could be delivered later, like additional host addresses. Prefer using 1624 * {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the 1625 * state of the service. 1626 */ 1627 @Deprecated resolveService(NsdServiceInfo serviceInfo, ResolveListener listener)1628 public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { 1629 resolveService(serviceInfo, Runnable::run, listener); 1630 } 1631 1632 /** 1633 * Resolve a discovered service. An application can resolve a service right before 1634 * establishing a connection to fetch the IP and port details on which to setup 1635 * the connection. 1636 * @param serviceInfo service to be resolved 1637 * @param executor Executor to run listener callbacks with 1638 * @param listener to receive callback upon success or failure. 1639 * 1640 * @deprecated the returned ServiceInfo may get stale at any time after resolution, including 1641 * immediately after the callback is called, and may not contain some service information that 1642 * could be delivered later, like additional host addresses. Prefer using 1643 * {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the 1644 * state of the service. 1645 */ 1646 @Deprecated resolveService(@onNull NsdServiceInfo serviceInfo, @NonNull Executor executor, @NonNull ResolveListener listener)1647 public void resolveService(@NonNull NsdServiceInfo serviceInfo, 1648 @NonNull Executor executor, @NonNull ResolveListener listener) { 1649 checkServiceInfoForResolution(serviceInfo); 1650 int key = putListener(listener, executor, serviceInfo); 1651 try { 1652 mService.resolveService(key, serviceInfo); 1653 } catch (RemoteException e) { 1654 e.rethrowFromSystemServer(); 1655 } 1656 } 1657 1658 /** 1659 * Stop service resolution initiated with {@link #resolveService}. 1660 * 1661 * A successful stop is notified with a call to {@link ResolveListener#onResolutionStopped}. 1662 * 1663 * <p> Upon failure to stop service resolution for example if resolution is done or the 1664 * requester stops resolution repeatedly, the application is notified 1665 * {@link ResolveListener#onStopResolutionFailed} with {@link #FAILURE_OPERATION_NOT_RUNNING} 1666 * 1667 * @param listener This should be a listener object that was passed to {@link #resolveService}. 1668 * It identifies the resolution that should be stopped and notifies of a 1669 * successful or unsuccessful stop. Throws {@code IllegalArgumentException} if 1670 * the listener was not passed to resolveService before. 1671 */ stopServiceResolution(@onNull ResolveListener listener)1672 public void stopServiceResolution(@NonNull ResolveListener listener) { 1673 int id = getListenerKey(listener); 1674 try { 1675 mService.stopResolution(id); 1676 } catch (RemoteException e) { 1677 e.rethrowFromSystemServer(); 1678 } 1679 } 1680 1681 /** 1682 * Register a callback to listen for updates to a service. 1683 * 1684 * An application can listen to a service to continuously monitor availability of given service. 1685 * The callback methods will be called on the passed executor. And service updates are sent with 1686 * continuous calls to {@link ServiceInfoCallback#onServiceUpdated}. 1687 * 1688 * This is different from {@link #resolveService} which provides one shot service information. 1689 * 1690 * <p> An application can listen to a service once a time. It needs to cancel the registration 1691 * before registering other callbacks. Upon failure to register a callback for example if 1692 * it's a duplicated registration, the application is notified through 1693 * {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed} with 1694 * {@link #FAILURE_BAD_PARAMETERS}. 1695 * 1696 * @param serviceInfo the service to receive updates for 1697 * @param executor Executor to run callbacks with 1698 * @param listener to receive callback upon service update 1699 */ 1700 // TODO: use {@link DiscoveryRequest} to specify the service to be subscribed registerServiceInfoCallback(@onNull NsdServiceInfo serviceInfo, @NonNull Executor executor, @NonNull ServiceInfoCallback listener)1701 public void registerServiceInfoCallback(@NonNull NsdServiceInfo serviceInfo, 1702 @NonNull Executor executor, @NonNull ServiceInfoCallback listener) { 1703 checkServiceInfoForResolution(serviceInfo); 1704 int key = putListener(listener, executor, serviceInfo); 1705 try { 1706 mService.registerServiceInfoCallback(key, serviceInfo); 1707 } catch (RemoteException e) { 1708 e.rethrowFromSystemServer(); 1709 } 1710 } 1711 1712 /** 1713 * Unregister a callback registered with {@link #registerServiceInfoCallback}. 1714 * 1715 * A successful unregistration is notified with a call to 1716 * {@link ServiceInfoCallback#onServiceInfoCallbackUnregistered}. The same callback can only be 1717 * reused after this is called. 1718 * 1719 * <p>If the callback is not already registered, this will throw with 1720 * {@link IllegalArgumentException}. 1721 * 1722 * @param listener This should be a listener object that was passed to 1723 * {@link #registerServiceInfoCallback}. It identifies the registration that 1724 * should be unregistered and notifies of a successful or unsuccessful stop. 1725 * Throws {@code IllegalArgumentException} if the listener was not passed to 1726 * {@link #registerServiceInfoCallback} before. 1727 */ unregisterServiceInfoCallback(@onNull ServiceInfoCallback listener)1728 public void unregisterServiceInfoCallback(@NonNull ServiceInfoCallback listener) { 1729 // Will throw IllegalArgumentException if the listener is not known 1730 int id = getListenerKey(listener); 1731 try { 1732 mService.unregisterServiceInfoCallback(id); 1733 } catch (RemoteException e) { 1734 e.rethrowFromSystemServer(); 1735 } 1736 } 1737 checkListener(Object listener)1738 private static void checkListener(Object listener) { 1739 Objects.requireNonNull(listener, "listener cannot be null"); 1740 } 1741 checkProtocol(int protocolType)1742 static void checkProtocol(int protocolType) { 1743 if (protocolType != PROTOCOL_DNS_SD) { 1744 throw new IllegalArgumentException("Unsupported protocol"); 1745 } 1746 } 1747 checkServiceInfoForResolution(NsdServiceInfo serviceInfo)1748 private static void checkServiceInfoForResolution(NsdServiceInfo serviceInfo) { 1749 Objects.requireNonNull(serviceInfo, "NsdServiceInfo cannot be null"); 1750 if (TextUtils.isEmpty(serviceInfo.getServiceName())) { 1751 throw new IllegalArgumentException("Service name cannot be empty"); 1752 } 1753 if (TextUtils.isEmpty(serviceInfo.getServiceType())) { 1754 throw new IllegalArgumentException("Service type cannot be empty"); 1755 } 1756 } 1757 1758 private enum ServiceValidationType { 1759 NO_SERVICE, 1760 HAS_SERVICE, // A service with a positive port 1761 HAS_SERVICE_ZERO_PORT, // A service with a zero port 1762 } 1763 1764 private enum HostValidationType { 1765 DEFAULT_HOST, // No host is specified so the default host will be used 1766 CUSTOM_HOST, // A custom host with addresses is specified 1767 CUSTOM_HOST_NO_ADDRESS, // A custom host without address is specified 1768 } 1769 1770 private enum PublicKeyValidationType { 1771 NO_KEY, 1772 HAS_KEY, 1773 } 1774 1775 /** 1776 * Check if the service is valid for registration and classify it as one of {@link 1777 * ServiceValidationType}. 1778 */ validateService(NsdServiceInfo serviceInfo)1779 private static ServiceValidationType validateService(NsdServiceInfo serviceInfo) { 1780 final boolean hasServiceName = !TextUtils.isEmpty(serviceInfo.getServiceName()); 1781 final boolean hasServiceType = !TextUtils.isEmpty(serviceInfo.getServiceType()); 1782 if (!hasServiceName && !hasServiceType && serviceInfo.getPort() == 0) { 1783 return ServiceValidationType.NO_SERVICE; 1784 } 1785 if (!hasServiceName || !hasServiceType) { 1786 throw new IllegalArgumentException("The service name or the service type is missing"); 1787 } 1788 if (serviceInfo.getPort() < 0) { 1789 throw new IllegalArgumentException("Invalid port"); 1790 } 1791 if (serviceInfo.getPort() == 0) { 1792 return ServiceValidationType.HAS_SERVICE_ZERO_PORT; 1793 } 1794 return ServiceValidationType.HAS_SERVICE; 1795 } 1796 1797 /** 1798 * Check if the host is valid for registration and classify it as one of {@link 1799 * HostValidationType}. 1800 */ validateHost(NsdServiceInfo serviceInfo)1801 private static HostValidationType validateHost(NsdServiceInfo serviceInfo) { 1802 final boolean hasHostname = !TextUtils.isEmpty(serviceInfo.getHostname()); 1803 final boolean hasHostAddresses = !CollectionUtils.isEmpty(serviceInfo.getHostAddresses()); 1804 if (!hasHostname) { 1805 // Keep compatible with the legacy behavior: It's allowed to set host 1806 // addresses for a service registration although the host addresses 1807 // won't be registered. To register the addresses for a host, the 1808 // hostname must be specified. 1809 return HostValidationType.DEFAULT_HOST; 1810 } 1811 if (!hasHostAddresses) { 1812 return HostValidationType.CUSTOM_HOST_NO_ADDRESS; 1813 } 1814 return HostValidationType.CUSTOM_HOST; 1815 } 1816 1817 /** 1818 * Check if the public key is valid for registration and classify it as one of {@link 1819 * PublicKeyValidationType}. 1820 * 1821 * <p>For simplicity, it only checks if the protocol is DNSSEC and the RDATA is not fewer than 4 1822 * bytes. See RFC 3445 Section 3. 1823 */ validatePublicKey(NsdServiceInfo serviceInfo)1824 private static PublicKeyValidationType validatePublicKey(NsdServiceInfo serviceInfo) { 1825 byte[] publicKey = serviceInfo.getPublicKey(); 1826 if (publicKey == null) { 1827 return PublicKeyValidationType.NO_KEY; 1828 } 1829 if (publicKey.length < 4) { 1830 throw new IllegalArgumentException("The public key should be at least 4 bytes long"); 1831 } 1832 int protocol = publicKey[2]; 1833 if (protocol == DNSSEC_PROTOCOL) { 1834 return PublicKeyValidationType.HAS_KEY; 1835 } 1836 throw new IllegalArgumentException( 1837 "The public key's protocol (" 1838 + protocol 1839 + ") is invalid. It should be DNSSEC_PROTOCOL (3)"); 1840 } 1841 1842 /** 1843 * Check if the {@link NsdServiceInfo} is valid for registration. 1844 * 1845 * <p>Firstly, check if service, host and public key are all valid respectively. Then check if 1846 * the combination of service, host and public key is valid. 1847 * 1848 * <p>If the {@code serviceInfo} is invalid, throw an {@link IllegalArgumentException} 1849 * describing the reason. 1850 * 1851 * <p>There are the invalid combinations of service, host and public key: 1852 * 1853 * <ul> 1854 * <li>Neither service nor host is specified. 1855 * <li>No public key is specified and the service has a zero port. 1856 * <li>The registration only contains the hostname but addresses are missing. 1857 * </ul> 1858 * 1859 * <p>Keys are used to reserve hostnames or service names while the service/host is temporarily 1860 * inactive, so registrations with a key and just a hostname or a service name are acceptable. 1861 * 1862 * @hide 1863 */ checkServiceInfoForRegistration(NsdServiceInfo serviceInfo)1864 public static void checkServiceInfoForRegistration(NsdServiceInfo serviceInfo) { 1865 Objects.requireNonNull(serviceInfo, "NsdServiceInfo cannot be null"); 1866 1867 final ServiceValidationType serviceValidation = validateService(serviceInfo); 1868 final HostValidationType hostValidation = validateHost(serviceInfo); 1869 final PublicKeyValidationType publicKeyValidation = validatePublicKey(serviceInfo); 1870 1871 if (serviceValidation == ServiceValidationType.NO_SERVICE 1872 && hostValidation == HostValidationType.DEFAULT_HOST) { 1873 throw new IllegalArgumentException("Nothing to register"); 1874 } 1875 if (publicKeyValidation == PublicKeyValidationType.NO_KEY) { 1876 if (serviceValidation == ServiceValidationType.HAS_SERVICE_ZERO_PORT) { 1877 throw new IllegalArgumentException("The port is missing"); 1878 } 1879 if (serviceValidation == ServiceValidationType.NO_SERVICE 1880 && hostValidation == HostValidationType.CUSTOM_HOST_NO_ADDRESS) { 1881 throw new IllegalArgumentException( 1882 "The host addresses must be specified unless there is a service"); 1883 } 1884 } 1885 } 1886 } 1887