• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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