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