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