1 /*
2  * Copyright (C) 2012 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 android.annotation.SdkConstant;
20 import android.annotation.SystemService;
21 import android.annotation.SdkConstant.SdkConstantType;
22 import android.content.Context;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.RemoteException;
28 import android.os.Messenger;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.util.SparseArray;
32 
33 import java.util.concurrent.CountDownLatch;
34 
35 import com.android.internal.util.AsyncChannel;
36 import com.android.internal.util.Protocol;
37 
38 /**
39  * The Network Service Discovery Manager class provides the API to discover services
40  * on a network. As an example, if device A and device B are connected over a Wi-Fi
41  * network, a game registered on device A can be discovered by a game on device
42  * B. Another example use case is an application discovering printers on the network.
43  *
44  * <p> The API currently supports DNS based service discovery and discovery is currently
45  * limited to a local network over Multicast DNS. DNS service discovery is described at
46  * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
47  *
48  * <p> The API is asynchronous and responses to requests from an application are on listener
49  * callbacks on a seperate internal thread.
50  *
51  * <p> There are three main operations the API supports - registration, discovery and resolution.
52  * <pre>
53  *                          Application start
54  *                                 |
55  *                                 |
56  *                                 |                  onServiceRegistered()
57  *                     Register any local services  /
58  *                      to be advertised with       \
59  *                       registerService()            onRegistrationFailed()
60  *                                 |
61  *                                 |
62  *                          discoverServices()
63  *                                 |
64  *                      Maintain a list to track
65  *                        discovered services
66  *                                 |
67  *                                 |--------->
68  *                                 |          |
69  *                                 |      onServiceFound()
70  *                                 |          |
71  *                                 |     add service to list
72  *                                 |          |
73  *                                 |<----------
74  *                                 |
75  *                                 |--------->
76  *                                 |          |
77  *                                 |      onServiceLost()
78  *                                 |          |
79  *                                 |   remove service from list
80  *                                 |          |
81  *                                 |<----------
82  *                                 |
83  *                                 |
84  *                                 | Connect to a service
85  *                                 | from list ?
86  *                                 |
87  *                          resolveService()
88  *                                 |
89  *                         onServiceResolved()
90  *                                 |
91  *                     Establish connection to service
92  *                     with the host and port information
93  *
94  * </pre>
95  * An application that needs to advertise itself over a network for other applications to
96  * discover it can do so with a call to {@link #registerService}. If Example is a http based
97  * application that can provide HTML data to peer services, it can register a name "Example"
98  * with service type "_http._tcp". A successful registration is notified with a callback to
99  * {@link RegistrationListener#onServiceRegistered} and a failure to register is notified
100  * over {@link RegistrationListener#onRegistrationFailed}
101  *
102  * <p> A peer application looking for http services can initiate a discovery for "_http._tcp"
103  * with a call to {@link #discoverServices}. A service found is notified with a callback
104  * to {@link DiscoveryListener#onServiceFound} and a service lost is notified on
105  * {@link DiscoveryListener#onServiceLost}.
106  *
107  * <p> Once the peer application discovers the "Example" http service, and either needs to read the
108  * attributes of the service or wants to receive data from the "Example" application, it can
109  * initiate a resolve with {@link #resolveService} to resolve the attributes, host, and port
110  * details. A successful resolve is notified on {@link ResolveListener#onServiceResolved} and a
111  * failure is notified on {@link ResolveListener#onResolveFailed}.
112  *
113  * Applications can reserve for a service type at
114  * http://www.iana.org/form/ports-service. Existing services can be found at
115  * http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml
116  *
117  * {@see NsdServiceInfo}
118  */
119 @SystemService(Context.NSD_SERVICE)
120 public final class NsdManager {
121     private static final String TAG = NsdManager.class.getSimpleName();
122     private static final boolean DBG = false;
123 
124     /**
125      * Broadcast intent action to indicate whether network service discovery is
126      * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state
127      * information as int.
128      *
129      * @see #EXTRA_NSD_STATE
130      */
131     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
132     public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED";
133 
134     /**
135      * The lookup key for an int that indicates whether network service discovery is enabled
136      * or disabled. Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}.
137      *
138      * @see #NSD_STATE_DISABLED
139      * @see #NSD_STATE_ENABLED
140      */
141     public static final String EXTRA_NSD_STATE = "nsd_state";
142 
143     /**
144      * Network service discovery is disabled
145      *
146      * @see #ACTION_NSD_STATE_CHANGED
147      */
148     public static final int NSD_STATE_DISABLED = 1;
149 
150     /**
151      * Network service discovery is enabled
152      *
153      * @see #ACTION_NSD_STATE_CHANGED
154      */
155     public static final int NSD_STATE_ENABLED = 2;
156 
157     private static final int BASE = Protocol.BASE_NSD_MANAGER;
158 
159     /** @hide */
160     public static final int DISCOVER_SERVICES                       = BASE + 1;
161     /** @hide */
162     public static final int DISCOVER_SERVICES_STARTED               = BASE + 2;
163     /** @hide */
164     public static final int DISCOVER_SERVICES_FAILED                = BASE + 3;
165     /** @hide */
166     public static final int SERVICE_FOUND                           = BASE + 4;
167     /** @hide */
168     public static final int SERVICE_LOST                            = BASE + 5;
169 
170     /** @hide */
171     public static final int STOP_DISCOVERY                          = BASE + 6;
172     /** @hide */
173     public static final int STOP_DISCOVERY_FAILED                   = BASE + 7;
174     /** @hide */
175     public static final int STOP_DISCOVERY_SUCCEEDED                = BASE + 8;
176 
177     /** @hide */
178     public static final int REGISTER_SERVICE                        = BASE + 9;
179     /** @hide */
180     public static final int REGISTER_SERVICE_FAILED                 = BASE + 10;
181     /** @hide */
182     public static final int REGISTER_SERVICE_SUCCEEDED              = BASE + 11;
183 
184     /** @hide */
185     public static final int UNREGISTER_SERVICE                      = BASE + 12;
186     /** @hide */
187     public static final int UNREGISTER_SERVICE_FAILED               = BASE + 13;
188     /** @hide */
189     public static final int UNREGISTER_SERVICE_SUCCEEDED            = BASE + 14;
190 
191     /** @hide */
192     public static final int RESOLVE_SERVICE                         = BASE + 18;
193     /** @hide */
194     public static final int RESOLVE_SERVICE_FAILED                  = BASE + 19;
195     /** @hide */
196     public static final int RESOLVE_SERVICE_SUCCEEDED               = BASE + 20;
197 
198     /** @hide */
199     public static final int ENABLE                                  = BASE + 24;
200     /** @hide */
201     public static final int DISABLE                                 = BASE + 25;
202 
203     /** @hide */
204     public static final int NATIVE_DAEMON_EVENT                     = BASE + 26;
205 
206     /** Dns based service discovery protocol */
207     public static final int PROTOCOL_DNS_SD = 0x0001;
208 
209     private static final SparseArray<String> EVENT_NAMES = new SparseArray<>();
210     static {
EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES")211         EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES");
EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED")212         EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED");
EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED")213         EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED");
EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND")214         EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND");
EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST")215         EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST");
EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY")216         EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY");
EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED")217         EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED");
EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED")218         EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED");
EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE")219         EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE");
EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED")220         EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED");
EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED")221         EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED");
EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE")222         EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE");
EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED")223         EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED");
EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED")224         EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED");
EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE")225         EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE");
EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED")226         EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED");
EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED")227         EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED");
EVENT_NAMES.put(ENABLE, "ENABLE")228         EVENT_NAMES.put(ENABLE, "ENABLE");
EVENT_NAMES.put(DISABLE, "DISABLE")229         EVENT_NAMES.put(DISABLE, "DISABLE");
EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT")230         EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT");
231     }
232 
233     /** @hide */
nameOf(int event)234     public static String nameOf(int event) {
235         String name = EVENT_NAMES.get(event);
236         if (name == null) {
237             return Integer.toString(event);
238         }
239         return name;
240     }
241 
242     private final INsdManager mService;
243     private final Context mContext;
244 
245     private static final int INVALID_LISTENER_KEY = 0;
246     private static final int BUSY_LISTENER_KEY = -1;
247     private int mListenerKey = 1;
248     private final SparseArray mListenerMap = new SparseArray();
249     private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
250     private final Object mMapLock = new Object();
251 
252     private final AsyncChannel mAsyncChannel = new AsyncChannel();
253     private ServiceHandler mHandler;
254     private final CountDownLatch mConnected = new CountDownLatch(1);
255 
256     /**
257      * Create a new Nsd instance. Applications use
258      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
259      * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}.
260      * @param service the Binder interface
261      * @hide - hide this because it takes in a parameter of type INsdManager, which
262      * is a system private class.
263      */
NsdManager(Context context, INsdManager service)264     public NsdManager(Context context, INsdManager service) {
265         mService = service;
266         mContext = context;
267         init();
268     }
269 
270     /**
271      * Failures are passed with {@link RegistrationListener#onRegistrationFailed},
272      * {@link RegistrationListener#onUnregistrationFailed},
273      * {@link DiscoveryListener#onStartDiscoveryFailed},
274      * {@link DiscoveryListener#onStopDiscoveryFailed} or {@link ResolveListener#onResolveFailed}.
275      *
276      * Indicates that the operation failed due to an internal error.
277      */
278     public static final int FAILURE_INTERNAL_ERROR               = 0;
279 
280     /**
281      * Indicates that the operation failed because it is already active.
282      */
283     public static final int FAILURE_ALREADY_ACTIVE              = 3;
284 
285     /**
286      * Indicates that the operation failed because the maximum outstanding
287      * requests from the applications have reached.
288      */
289     public static final int FAILURE_MAX_LIMIT                   = 4;
290 
291     /** Interface for callback invocation for service discovery */
292     public interface DiscoveryListener {
293 
onStartDiscoveryFailed(String serviceType, int errorCode)294         public void onStartDiscoveryFailed(String serviceType, int errorCode);
295 
onStopDiscoveryFailed(String serviceType, int errorCode)296         public void onStopDiscoveryFailed(String serviceType, int errorCode);
297 
onDiscoveryStarted(String serviceType)298         public void onDiscoveryStarted(String serviceType);
299 
onDiscoveryStopped(String serviceType)300         public void onDiscoveryStopped(String serviceType);
301 
onServiceFound(NsdServiceInfo serviceInfo)302         public void onServiceFound(NsdServiceInfo serviceInfo);
303 
onServiceLost(NsdServiceInfo serviceInfo)304         public void onServiceLost(NsdServiceInfo serviceInfo);
305 
306     }
307 
308     /** Interface for callback invocation for service registration */
309     public interface RegistrationListener {
310 
onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode)311         public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode);
312 
onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode)313         public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode);
314 
onServiceRegistered(NsdServiceInfo serviceInfo)315         public void onServiceRegistered(NsdServiceInfo serviceInfo);
316 
onServiceUnregistered(NsdServiceInfo serviceInfo)317         public void onServiceUnregistered(NsdServiceInfo serviceInfo);
318     }
319 
320     /** Interface for callback invocation for service resolution */
321     public interface ResolveListener {
322 
onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)323         public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode);
324 
onServiceResolved(NsdServiceInfo serviceInfo)325         public void onServiceResolved(NsdServiceInfo serviceInfo);
326     }
327 
328     private class ServiceHandler extends Handler {
ServiceHandler(Looper looper)329         ServiceHandler(Looper looper) {
330             super(looper);
331         }
332 
333         @Override
handleMessage(Message message)334         public void handleMessage(Message message) {
335             if (DBG) Log.d(TAG, "received " + nameOf(message.what));
336             switch (message.what) {
337                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
338                     mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
339                     return;
340                 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
341                     mConnected.countDown();
342                     return;
343                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
344                     Log.e(TAG, "Channel lost");
345                     return;
346                 default:
347                     break;
348             }
349             Object listener = getListener(message.arg2);
350             if (listener == null) {
351                 Log.d(TAG, "Stale key " + message.arg2);
352                 return;
353             }
354             NsdServiceInfo ns = getNsdService(message.arg2);
355             switch (message.what) {
356                 case DISCOVER_SERVICES_STARTED:
357                     String s = getNsdServiceInfoType((NsdServiceInfo) message.obj);
358                     ((DiscoveryListener) listener).onDiscoveryStarted(s);
359                     break;
360                 case DISCOVER_SERVICES_FAILED:
361                     removeListener(message.arg2);
362                     ((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns),
363                             message.arg1);
364                     break;
365                 case SERVICE_FOUND:
366                     ((DiscoveryListener) listener).onServiceFound((NsdServiceInfo) message.obj);
367                     break;
368                 case SERVICE_LOST:
369                     ((DiscoveryListener) listener).onServiceLost((NsdServiceInfo) message.obj);
370                     break;
371                 case STOP_DISCOVERY_FAILED:
372                     removeListener(message.arg2);
373                     ((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns),
374                             message.arg1);
375                     break;
376                 case STOP_DISCOVERY_SUCCEEDED:
377                     removeListener(message.arg2);
378                     ((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns));
379                     break;
380                 case REGISTER_SERVICE_FAILED:
381                     removeListener(message.arg2);
382                     ((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1);
383                     break;
384                 case REGISTER_SERVICE_SUCCEEDED:
385                     ((RegistrationListener) listener).onServiceRegistered(
386                             (NsdServiceInfo) message.obj);
387                     break;
388                 case UNREGISTER_SERVICE_FAILED:
389                     removeListener(message.arg2);
390                     ((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1);
391                     break;
392                 case UNREGISTER_SERVICE_SUCCEEDED:
393                     removeListener(message.arg2);
394                     ((RegistrationListener) listener).onServiceUnregistered(ns);
395                     break;
396                 case RESOLVE_SERVICE_FAILED:
397                     removeListener(message.arg2);
398                     ((ResolveListener) listener).onResolveFailed(ns, message.arg1);
399                     break;
400                 case RESOLVE_SERVICE_SUCCEEDED:
401                     removeListener(message.arg2);
402                     ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj);
403                     break;
404                 default:
405                     Log.d(TAG, "Ignored " + message);
406                     break;
407             }
408         }
409     }
410 
411     // if the listener is already in the map, reject it.  Otherwise, add it and
412     // return its key.
putListener(Object listener, NsdServiceInfo s)413     private int putListener(Object listener, NsdServiceInfo s) {
414         if (listener == null) return INVALID_LISTENER_KEY;
415         int key;
416         synchronized (mMapLock) {
417             int valueIndex = mListenerMap.indexOfValue(listener);
418             if (valueIndex != -1) {
419                 return BUSY_LISTENER_KEY;
420             }
421             do {
422                 key = mListenerKey++;
423             } while (key == INVALID_LISTENER_KEY);
424             mListenerMap.put(key, listener);
425             mServiceMap.put(key, s);
426         }
427         return key;
428     }
429 
getListener(int key)430     private Object getListener(int key) {
431         if (key == INVALID_LISTENER_KEY) return null;
432         synchronized (mMapLock) {
433             return mListenerMap.get(key);
434         }
435     }
436 
getNsdService(int key)437     private NsdServiceInfo getNsdService(int key) {
438         synchronized (mMapLock) {
439             return mServiceMap.get(key);
440         }
441     }
442 
removeListener(int key)443     private void removeListener(int key) {
444         if (key == INVALID_LISTENER_KEY) return;
445         synchronized (mMapLock) {
446             mListenerMap.remove(key);
447             mServiceMap.remove(key);
448         }
449     }
450 
getListenerKey(Object listener)451     private int getListenerKey(Object listener) {
452         synchronized (mMapLock) {
453             int valueIndex = mListenerMap.indexOfValue(listener);
454             if (valueIndex != -1) {
455                 return mListenerMap.keyAt(valueIndex);
456             }
457         }
458         return INVALID_LISTENER_KEY;
459     }
460 
getNsdServiceInfoType(NsdServiceInfo s)461     private String getNsdServiceInfoType(NsdServiceInfo s) {
462         if (s == null) return "?";
463         return s.getServiceType();
464     }
465 
466     /**
467      * Initialize AsyncChannel
468      */
init()469     private void init() {
470         final Messenger messenger = getMessenger();
471         if (messenger == null) throw new RuntimeException("Failed to initialize");
472         HandlerThread t = new HandlerThread("NsdManager");
473         t.start();
474         mHandler = new ServiceHandler(t.getLooper());
475         mAsyncChannel.connect(mContext, mHandler, messenger);
476         try {
477             mConnected.await();
478         } catch (InterruptedException e) {
479             Log.e(TAG, "interrupted wait at init");
480         }
481     }
482 
483     /**
484      * Register a service to be discovered by other services.
485      *
486      * <p> The function call immediately returns after sending a request to register service
487      * to the framework. The application is notified of a successful registration
488      * through the callback {@link RegistrationListener#onServiceRegistered} or a failure
489      * through {@link RegistrationListener#onRegistrationFailed}.
490      *
491      * <p> The application should call {@link #unregisterService} when the service
492      * registration is no longer required, and/or whenever the application is stopped.
493      *
494      * @param serviceInfo The service being registered
495      * @param protocolType The service discovery protocol
496      * @param listener The listener notifies of a successful registration and is used to
497      * unregister this service through a call on {@link #unregisterService}. Cannot be null.
498      * Cannot be in use for an active service registration.
499      */
registerService(NsdServiceInfo serviceInfo, int protocolType, RegistrationListener listener)500     public void registerService(NsdServiceInfo serviceInfo, int protocolType,
501             RegistrationListener listener) {
502         if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
503                 TextUtils.isEmpty(serviceInfo.getServiceType())) {
504             throw new IllegalArgumentException("Service name or type cannot be empty");
505         }
506         if (serviceInfo.getPort() <= 0) {
507             throw new IllegalArgumentException("Invalid port number");
508         }
509         if (listener == null) {
510             throw new IllegalArgumentException("listener cannot be null");
511         }
512         if (protocolType != PROTOCOL_DNS_SD) {
513             throw new IllegalArgumentException("Unsupported protocol");
514         }
515         int key = putListener(listener, serviceInfo);
516         if (key == BUSY_LISTENER_KEY) {
517             throw new IllegalArgumentException("listener already in use");
518         }
519         mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo);
520     }
521 
522     /**
523      * Unregister a service registered through {@link #registerService}. A successful
524      * unregister is notified to the application with a call to
525      * {@link RegistrationListener#onServiceUnregistered}.
526      *
527      * @param listener This should be the listener object that was passed to
528      * {@link #registerService}. It identifies the service that should be unregistered
529      * and notifies of a successful or unsuccessful unregistration via the listener
530      * callbacks.  In API versions 20 and above, the listener object may be used for
531      * another service registration once the callback has been called.  In API versions <= 19,
532      * there is no entirely reliable way to know when a listener may be re-used, and a new
533      * listener should be created for each service registration request.
534      */
unregisterService(RegistrationListener listener)535     public void unregisterService(RegistrationListener listener) {
536         int id = getListenerKey(listener);
537         if (id == INVALID_LISTENER_KEY) {
538             throw new IllegalArgumentException("listener not registered");
539         }
540         if (listener == null) {
541             throw new IllegalArgumentException("listener cannot be null");
542         }
543         mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id);
544     }
545 
546     /**
547      * Initiate service discovery to browse for instances of a service type. Service discovery
548      * consumes network bandwidth and will continue until the application calls
549      * {@link #stopServiceDiscovery}.
550      *
551      * <p> The function call immediately returns after sending a request to start service
552      * discovery to the framework. The application is notified of a success to initiate
553      * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure
554      * through {@link DiscoveryListener#onStartDiscoveryFailed}.
555      *
556      * <p> Upon successful start, application is notified when a service is found with
557      * {@link DiscoveryListener#onServiceFound} or when a service is lost with
558      * {@link DiscoveryListener#onServiceLost}.
559      *
560      * <p> Upon failure to start, service discovery is not active and application does
561      * not need to invoke {@link #stopServiceDiscovery}
562      *
563      * <p> The application should call {@link #stopServiceDiscovery} when discovery of this
564      * service type is no longer required, and/or whenever the application is paused or
565      * stopped.
566      *
567      * @param serviceType The service type being discovered. Examples include "_http._tcp" for
568      * http services or "_ipp._tcp" for printers
569      * @param protocolType The service discovery protocol
570      * @param listener  The listener notifies of a successful discovery and is used
571      * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
572      * Cannot be null. Cannot be in use for an active service discovery.
573      */
discoverServices(String serviceType, int protocolType, DiscoveryListener listener)574     public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
575         if (listener == null) {
576             throw new IllegalArgumentException("listener cannot be null");
577         }
578         if (TextUtils.isEmpty(serviceType)) {
579             throw new IllegalArgumentException("Service type cannot be empty");
580         }
581 
582         if (protocolType != PROTOCOL_DNS_SD) {
583             throw new IllegalArgumentException("Unsupported protocol");
584         }
585 
586         NsdServiceInfo s = new NsdServiceInfo();
587         s.setServiceType(serviceType);
588 
589         int key = putListener(listener, s);
590         if (key == BUSY_LISTENER_KEY) {
591             throw new IllegalArgumentException("listener already in use");
592         }
593 
594         mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s);
595     }
596 
597     /**
598      * Stop service discovery initiated with {@link #discoverServices}.  An active service
599      * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted}
600      * and it stays active until the application invokes a stop service discovery. A successful
601      * stop is notified to with a call to {@link DiscoveryListener#onDiscoveryStopped}.
602      *
603      * <p> Upon failure to stop service discovery, application is notified through
604      * {@link DiscoveryListener#onStopDiscoveryFailed}.
605      *
606      * @param listener This should be the listener object that was passed to {@link #discoverServices}.
607      * It identifies the discovery that should be stopped and notifies of a successful or
608      * unsuccessful stop.  In API versions 20 and above, the listener object may be used for
609      * another service discovery once the callback has been called.  In API versions <= 19,
610      * there is no entirely reliable way to know when a listener may be re-used, and a new
611      * listener should be created for each service discovery request.
612      */
stopServiceDiscovery(DiscoveryListener listener)613     public void stopServiceDiscovery(DiscoveryListener listener) {
614         int id = getListenerKey(listener);
615         if (id == INVALID_LISTENER_KEY) {
616             throw new IllegalArgumentException("service discovery not active on listener");
617         }
618         if (listener == null) {
619             throw new IllegalArgumentException("listener cannot be null");
620         }
621         mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id);
622     }
623 
624     /**
625      * Resolve a discovered service. An application can resolve a service right before
626      * establishing a connection to fetch the IP and port details on which to setup
627      * the connection.
628      *
629      * @param serviceInfo service to be resolved
630      * @param listener to receive callback upon success or failure. Cannot be null.
631      * Cannot be in use for an active service resolution.
632      */
resolveService(NsdServiceInfo serviceInfo, ResolveListener listener)633     public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
634         if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
635                 TextUtils.isEmpty(serviceInfo.getServiceType())) {
636             throw new IllegalArgumentException("Service name or type cannot be empty");
637         }
638         if (listener == null) {
639             throw new IllegalArgumentException("listener cannot be null");
640         }
641 
642         int key = putListener(listener, serviceInfo);
643 
644         if (key == BUSY_LISTENER_KEY) {
645             throw new IllegalArgumentException("listener already in use");
646         }
647         mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo);
648     }
649 
650     /** Internal use only @hide */
setEnabled(boolean enabled)651     public void setEnabled(boolean enabled) {
652         try {
653             mService.setEnabled(enabled);
654         } catch (RemoteException e) {
655             throw e.rethrowFromSystemServer();
656         }
657     }
658 
659     /**
660      * Get a reference to NetworkService handler. This is used to establish
661      * an AsyncChannel communication with the service
662      *
663      * @return Messenger pointing to the NetworkService handler
664      */
getMessenger()665     private Messenger getMessenger() {
666         try {
667             return mService.getMessenger();
668         } catch (RemoteException e) {
669             throw e.rethrowFromSystemServer();
670         }
671     }
672 }
673