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