NsdManager.java revision 92784670c48759c0db604ddb95c05a7b9bdebed8
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
17package android.net.nsd;
18
19import android.content.Context;
20import android.os.Binder;
21import android.os.IBinder;
22import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
25import android.os.RemoteException;
26import android.os.Messenger;
27import android.text.TextUtils;
28import android.util.Log;
29
30import com.android.internal.util.AsyncChannel;
31import com.android.internal.util.Protocol;
32
33/**
34 * The Network Service Discovery Manager class provides the API to discover services
35 * on a network. As an example, if device A and device B are connected over a Wi-Fi
36 * network, a game registered on device A can be discovered by a game on device
37 * B. Another example use case is an application discovering printers on the network.
38 *
39 * <p> The API currently supports DNS based service discovery and discovery is currently
40 * limited to a local network over Multicast DNS. In future, it will be extended to
41 * support wide area discovery and other service discovery mechanisms.
42 * DNS service discovery is described at http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
43 *
44 * <p> The API is asynchronous and responses to requests from an application are on listener
45 * callbacks provided by the application. The application must invoke {@link #initialize} before
46 * doing any other operation.
47 *
48 * <p> There are three main operations the API supports - registration, discovery and resolution.
49 * <pre>
50 *                          Application start
51 *                                 |
52 *                                 |         <----------------------------------------------
53 *                             initialize()                                                 |
54 *                                 |                                                        |
55 *                                 | Wait until channel connects                            |
56 *                                 | before doing any operation                             |
57 *                                 |                                                        |
58 *                           onChannelConnected()                    __________             |
59 *                                 |                                           |            |
60 *                                 |                                           |            |
61 *                                 |                  onServiceRegistered()    |            |
62 *                     Register any local services  /                          |            |
63 *                      to be advertised with       \                          |            | If application needs to
64 *                       registerService()            onFailure()              |            | do any further operations
65 *                                 |                                           |            | again, it needs to
66 *                                 |                                           |            | initialize() connection
67 *                          discoverServices()                                 |            | to framework again
68 *                                 |                                           |            |
69 *                      Maintain a list to track                               |            |
70 *                        discovered services                                  |            |
71 *                                 |                                           |            |
72 *                                 |--------->                                 |-> onChannelDisconnected()
73 *                                 |          |                                |
74 *                                 |      onServiceFound()                     |
75 *                                 |          |                                |
76 *                                 |     add service to list                   |
77 *                                 |          |                                |
78 *                                 |<----------                                |
79 *                                 |                                           |
80 *                                 |--------->                                 |
81 *                                 |          |                                |
82 *                                 |      onServiceLost()                      |
83 *                                 |          |                                |
84 *                                 |   remove service from list                |
85 *                                 |          |                                |
86 *                                 |<----------                                |
87 *                                 |                                           |
88 *                                 |                                           |
89 *                                 | Connect to a service                      |
90 *                                 | from list ?                               |
91 *                                 |                                           |
92 *                          resolveService()                                   |
93 *                                 |                                           |
94 *                         onServiceResolved()                                 |
95 *                                 |                                           |
96 *                     Establish connection to service                         |
97 *                     with the host and port information                      |
98 *                                 |                                           |
99 *                                 |                                ___________|
100 *                           deinitialize()
101 *                    when done with all operations
102 *                          or before quit
103 *
104 * </pre>
105 * An application that needs to advertise itself over a network for other applications to
106 * discover it can do so with a call to {@link #registerService}. If Example is a http based
107 * application that can provide HTML data to peer services, it can register a name "Example"
108 * with service type "_http._tcp". A successful registration is notified with a callback to
109 * {@link DnsSdRegisterListener#onServiceRegistered} and a failure to register is notified
110 * over {@link DnsSdRegisterListener#onFailure}
111 *
112 * <p> A peer application looking for http services can initiate a discovery for "_http._tcp"
113 * with a call to {@link #discoverServices}. A service found is notified with a callback
114 * to {@link DnsSdDiscoveryListener#onServiceFound} and a service lost is notified on
115 * {@link DnsSdDiscoveryListener#onServiceLost}.
116 *
117 * <p> Once the peer application discovers the "Example" http srevice, and needs to receive data
118 * from the "Example" application, it can initiate a resolve with {@link #resolveService} to
119 * resolve the host and port details for the purpose of establishing a connection. A successful
120 * resolve is notified on {@link DnsSdResolveListener#onServiceResolved} and a failure is notified
121 * on {@link DnsSdResolveListener#onFailure}.
122 *
123 * Applications can reserve for a service type at
124 * http://www.iana.org/form/ports-service. Existing services can be found at
125 * http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml
126 *
127 * Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
128 * Context.getSystemService(Context.NSD_SERVICE)}.
129 *
130 * {@see DnsSdServiceInfo}
131 */
132public class NsdManager {
133    private static final String TAG = "NsdManager";
134    INsdManager mService;
135
136    private static final int BASE = Protocol.BASE_NSD_MANAGER;
137
138    /** @hide */
139    public static final int DISCOVER_SERVICES                       = BASE + 1;
140    /** @hide */
141    public static final int DISCOVER_SERVICES_STARTED               = BASE + 2;
142    /** @hide */
143    public static final int DISCOVER_SERVICES_FAILED                = BASE + 3;
144    /** @hide */
145    public static final int SERVICE_FOUND                           = BASE + 4;
146    /** @hide */
147    public static final int SERVICE_LOST                            = BASE + 5;
148
149    /** @hide */
150    public static final int STOP_DISCOVERY                          = BASE + 6;
151    /** @hide */
152    public static final int STOP_DISCOVERY_FAILED                   = BASE + 7;
153    /** @hide */
154    public static final int STOP_DISCOVERY_SUCCEEDED                = BASE + 8;
155
156    /** @hide */
157    public static final int REGISTER_SERVICE                        = BASE + 9;
158    /** @hide */
159    public static final int REGISTER_SERVICE_FAILED                 = BASE + 10;
160    /** @hide */
161    public static final int REGISTER_SERVICE_SUCCEEDED              = BASE + 11;
162
163    /** @hide */
164    public static final int UNREGISTER_SERVICE                      = BASE + 12;
165    /** @hide */
166    public static final int UNREGISTER_SERVICE_FAILED               = BASE + 13;
167    /** @hide */
168    public static final int UNREGISTER_SERVICE_SUCCEEDED            = BASE + 14;
169
170    /** @hide */
171    public static final int UPDATE_SERVICE                          = BASE + 15;
172    /** @hide */
173    public static final int UPDATE_SERVICE_FAILED                   = BASE + 16;
174    /** @hide */
175    public static final int UPDATE_SERVICE_SUCCEEDED                = BASE + 17;
176
177    /** @hide */
178    public static final int RESOLVE_SERVICE                         = BASE + 18;
179    /** @hide */
180    public static final int RESOLVE_SERVICE_FAILED                  = BASE + 19;
181    /** @hide */
182    public static final int RESOLVE_SERVICE_SUCCEEDED               = BASE + 20;
183
184    /** @hide */
185    public static final int STOP_RESOLVE                            = BASE + 21;
186    /** @hide */
187    public static final int STOP_RESOLVE_FAILED                     = BASE + 22;
188    /** @hide */
189    public static final int STOP_RESOLVE_SUCCEEDED                  = BASE + 23;
190
191    /**
192     * Create a new Nsd instance. Applications use
193     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
194     * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}.
195     * @param service the Binder interface
196     * @hide - hide this because it takes in a parameter of type INsdManager, which
197     * is a system private class.
198     */
199    public NsdManager(INsdManager service) {
200        mService = service;
201    }
202
203    /**
204     * Passed with onFailure() calls.
205     * Indicates that the operation failed due to an internal error.
206     */
207    public static final int ERROR               = 0;
208
209    /**
210     * Passed with onFailure() calls.
211     * Indicates that the operation failed because service discovery
212     * is unsupported on the device.
213     */
214    public static final int UNSUPPORTED         = 1;
215
216    /**
217     * Passed with onFailure() calls.
218     * Indicates that the operation failed because the framework is
219     * busy and unable to service the request.
220     */
221    public static final int BUSY                = 2;
222
223    /**
224     * Passed with onFailure() calls.
225     * Indicates that the operation failed because it is already active.
226     */
227    public static final int ALREADY_ACTIVE      = 3;
228
229    /**
230     * Passed with onFailure() calls.
231     * Indicates that the operation failed because maximum limit on
232     * service registrations has reached.
233     */
234    public static final int MAX_REGS_REACHED    = 4;
235
236    /** Interface for callback invocation when framework channel is connected or lost */
237    public interface ChannelListener {
238       /**
239         * The channel to the framework is connected.
240         * Application can initiate calls into the framework using the channel instance passed.
241         */
242        public void onChannelConnected(Channel c);
243        /**
244         * The channel to the framework has been disconnected.
245         * Application could try re-initializing using {@link #initialize}
246         */
247        public void onChannelDisconnected();
248    }
249
250    /** Generic interface for callback invocation for a success or failure */
251    public interface ActionListener {
252
253        public void onFailure(int errorCode);
254
255        public void onSuccess();
256    }
257
258    /** Interface for callback invocation for service discovery */
259    public interface DnsSdDiscoveryListener {
260
261        public void onFailure(int errorCode);
262
263        public void onStarted(String serviceType);
264
265        public void onServiceFound(DnsSdServiceInfo serviceInfo);
266
267        public void onServiceLost(DnsSdServiceInfo serviceInfo);
268
269    }
270
271    /** Interface for callback invocation for service registration */
272    public interface DnsSdRegisterListener {
273
274        public void onFailure(int errorCode);
275
276        public void onServiceRegistered(int registeredId, DnsSdServiceInfo serviceInfo);
277    }
278
279    /** @hide */
280    public interface DnsSdUpdateRegistrationListener {
281
282        public void onFailure(int errorCode);
283
284        public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord);
285    }
286
287    /** Interface for callback invocation for service resolution */
288    public interface DnsSdResolveListener {
289
290        public void onFailure(int errorCode);
291
292        public void onServiceResolved(DnsSdServiceInfo serviceInfo);
293    }
294
295    /**
296     * A channel that connects the application to the NetworkService framework.
297     * Most service operations require a Channel as an argument. An instance of Channel is obtained
298     * by doing a call on {@link #initialize}
299     */
300    public static class Channel {
301        Channel(Looper looper, ChannelListener l) {
302            mAsyncChannel = new AsyncChannel();
303            mHandler = new ServiceHandler(looper);
304            mChannelListener = l;
305        }
306        private ChannelListener mChannelListener;
307        private DnsSdDiscoveryListener mDnsSdDiscoveryListener;
308        private ActionListener mDnsSdStopDiscoveryListener;
309        private DnsSdRegisterListener mDnsSdRegisterListener;
310        private ActionListener mDnsSdUnregisterListener;
311        private DnsSdUpdateRegistrationListener mDnsSdUpdateListener;
312        private DnsSdResolveListener mDnsSdResolveListener;
313        private ActionListener mDnsSdStopResolveListener;
314
315        AsyncChannel mAsyncChannel;
316        ServiceHandler mHandler;
317        class ServiceHandler extends Handler {
318            ServiceHandler(Looper looper) {
319                super(looper);
320            }
321
322            @Override
323            public void handleMessage(Message message) {
324                switch (message.what) {
325                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
326                        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
327                        break;
328                    case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
329                        if (mChannelListener != null) {
330                            mChannelListener.onChannelConnected(Channel.this);
331                        }
332                        break;
333                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
334                        if (mChannelListener != null) {
335                            mChannelListener.onChannelDisconnected();
336                            mChannelListener = null;
337                        }
338                        break;
339                    case DISCOVER_SERVICES_STARTED:
340                        if (mDnsSdDiscoveryListener != null) {
341                            mDnsSdDiscoveryListener.onStarted((String) message.obj);
342                        }
343                        break;
344                    case DISCOVER_SERVICES_FAILED:
345                        if (mDnsSdDiscoveryListener != null) {
346                            mDnsSdDiscoveryListener.onFailure(message.arg1);
347                        }
348                        break;
349                    case SERVICE_FOUND:
350                        if (mDnsSdDiscoveryListener != null) {
351                            mDnsSdDiscoveryListener.onServiceFound(
352                                    (DnsSdServiceInfo) message.obj);
353                        }
354                        break;
355                    case SERVICE_LOST:
356                        if (mDnsSdDiscoveryListener != null) {
357                            mDnsSdDiscoveryListener.onServiceLost(
358                                    (DnsSdServiceInfo) message.obj);
359                        }
360                        break;
361                    case STOP_DISCOVERY_FAILED:
362                        if (mDnsSdStopDiscoveryListener != null) {
363                            mDnsSdStopDiscoveryListener.onFailure(message.arg1);
364                        }
365                        break;
366                    case STOP_DISCOVERY_SUCCEEDED:
367                        if (mDnsSdStopDiscoveryListener != null) {
368                            mDnsSdStopDiscoveryListener.onSuccess();
369                        }
370                        break;
371                    case REGISTER_SERVICE_FAILED:
372                        if (mDnsSdRegisterListener != null) {
373                            mDnsSdRegisterListener.onFailure(message.arg1);
374                        }
375                        break;
376                    case REGISTER_SERVICE_SUCCEEDED:
377                        if (mDnsSdRegisterListener != null) {
378                            mDnsSdRegisterListener.onServiceRegistered(message.arg1,
379                                    (DnsSdServiceInfo) message.obj);
380                        }
381                        break;
382                    case UNREGISTER_SERVICE_FAILED:
383                        if (mDnsSdUnregisterListener != null) {
384                            mDnsSdUnregisterListener.onFailure(message.arg1);
385                        }
386                        break;
387                    case UNREGISTER_SERVICE_SUCCEEDED:
388                        if (mDnsSdUnregisterListener != null) {
389                            mDnsSdUnregisterListener.onSuccess();
390                        }
391                        break;
392                   case UPDATE_SERVICE_FAILED:
393                        if (mDnsSdUpdateListener != null) {
394                            mDnsSdUpdateListener.onFailure(message.arg1);
395                        }
396                        break;
397                    case UPDATE_SERVICE_SUCCEEDED:
398                        if (mDnsSdUpdateListener != null) {
399                            mDnsSdUpdateListener.onServiceUpdated(message.arg1,
400                                    (DnsSdTxtRecord) message.obj);
401                        }
402                        break;
403                    case RESOLVE_SERVICE_FAILED:
404                        if (mDnsSdResolveListener != null) {
405                            mDnsSdResolveListener.onFailure(message.arg1);
406                        }
407                        break;
408                    case RESOLVE_SERVICE_SUCCEEDED:
409                        if (mDnsSdResolveListener != null) {
410                            mDnsSdResolveListener.onServiceResolved(
411                                    (DnsSdServiceInfo) message.obj);
412                        }
413                        break;
414                    case STOP_RESOLVE_FAILED:
415                        if (mDnsSdStopResolveListener!= null) {
416                            mDnsSdStopResolveListener.onFailure(message.arg1);
417                        }
418                        break;
419                    case STOP_RESOLVE_SUCCEEDED:
420                        if (mDnsSdStopResolveListener != null) {
421                            mDnsSdStopResolveListener.onSuccess();
422                        }
423                        break;
424                    default:
425                        Log.d(TAG, "Ignored " + message);
426                        break;
427                }
428            }
429        }
430   }
431
432    private static void checkChannel(Channel c) {
433        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
434    }
435
436    /**
437     * Registers the application with the service discovery framework. This function
438     * must be the first to be called before any other operations are performed. No service
439     * discovery operations must be performed until the ChannelListener callback notifies
440     * that the channel is connected
441     *
442     * @param srcContext is the context of the source
443     * @param srcLooper is the Looper on which the callbacks are receivied
444     * @param listener for callback at loss of framework communication. Cannot be null.
445     */
446    public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
447        Messenger messenger = getMessenger();
448        if (messenger == null) throw new RuntimeException("Failed to initialize");
449        if (listener == null) throw new IllegalArgumentException("ChannelListener cannot be null");
450
451        Channel c = new Channel(srcLooper, listener);
452        c.mAsyncChannel.connect(srcContext, c.mHandler, messenger);
453    }
454
455    /**
456     * Disconnects application from service discovery framework. No further operations
457     * will succeed until a {@link #initialize} is called again.
458     *
459     * @param c channel initialized with {@link #initialize}
460     */
461    public void deinitialize(Channel c) {
462        checkChannel(c);
463        c.mAsyncChannel.disconnect();
464    }
465
466    /**
467     * Register a service to be discovered by other services.
468     *
469     * <p> The function call immediately returns after sending a request to register service
470     * to the framework. The application is notified of a success to initiate
471     * discovery through the callback {@link DnsSdRegisterListener#onServiceRegistered} or a failure
472     * through {@link DnsSdRegisterListener#onFailure}.
473     *
474     * @param c is the channel created at {@link #initialize}
475     * @param serviceType The service type being advertised.
476     * @param port on which the service is listenering for incoming connections
477     * @param listener for success or failure callback. Can be null.
478     */
479    public void registerService(Channel c, String serviceName, String serviceType, int port,
480            DnsSdRegisterListener listener) {
481        checkChannel(c);
482        if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) {
483            throw new IllegalArgumentException("Service name or type cannot be empty");
484        }
485        if (port <= 0) {
486            throw new IllegalArgumentException("Invalid port number");
487        }
488        DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null);
489        serviceInfo.setPort(port);
490        c.mDnsSdRegisterListener = listener;
491        c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo);
492    }
493
494    /**
495     * Unregister a service registered through {@link #registerService}
496     * @param c is the channel created at {@link #initialize}
497     * @param registeredId is obtained at {@link DnsSdRegisterListener#onServiceRegistered}
498     * @param listener provides callbacks for success or failure. Can be null.
499     */
500    public void unregisterService(Channel c, int registeredId, ActionListener listener) {
501        checkChannel(c);
502        c.mDnsSdUnregisterListener = listener;
503        c.mAsyncChannel.sendMessage(UNREGISTER_SERVICE, registeredId);
504    }
505
506    /** @hide */
507    public void updateService(Channel c, int registeredId, DnsSdTxtRecord txtRecord) {
508        checkChannel(c);
509        c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord);
510    }
511
512    /**
513     * Initiate service discovery to browse for instances of a service type. Service discovery
514     * consumes network bandwidth and will continue until the application calls
515     * {@link #stopServiceDiscovery}.
516     *
517     * <p> The function call immediately returns after sending a request to start service
518     * discovery to the framework. The application is notified of a success to initiate
519     * discovery through the callback {@link DnsSdDiscoveryListener#onStarted} or a failure
520     * through {@link DnsSdDiscoveryListener#onFailure}.
521     *
522     * <p> Upon successful start, application is notified when a service is found with
523     * {@link DnsSdDiscoveryListener#onServiceFound} or when a service is lost with
524     * {@link DnsSdDiscoveryListener#onServiceLost}.
525     *
526     * <p> Upon failure to start, service discovery is not active and application does
527     * not need to invoke {@link #stopServiceDiscovery}
528     *
529     * @param c is the channel created at {@link #initialize}
530     * @param serviceType The service type being discovered. Examples include "_http._tcp" for
531     * http services or "_ipp._tcp" for printers
532     * @param listener provides callbacks when service is found or lost. Cannot be null.
533     */
534    public void discoverServices(Channel c, String serviceType, DnsSdDiscoveryListener listener) {
535        checkChannel(c);
536        if (listener == null) {
537            throw new IllegalStateException("Discovery listener needs to be set first");
538        }
539        if (TextUtils.isEmpty(serviceType)) {
540            throw new IllegalStateException("Service type cannot be empty");
541        }
542        DnsSdServiceInfo s = new DnsSdServiceInfo();
543        s.setServiceType(serviceType);
544        c.mDnsSdDiscoveryListener = listener;
545        c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s);
546    }
547
548    /**
549     * Stop service discovery initiated with {@link #discoverServices}. An active service
550     * discovery is notified to the application with {@link DnsSdDiscoveryListener#onStarted}
551     * and it stays active until the application invokes a stop service discovery.
552     *
553     * <p> Upon failure to start service discovery notified through
554     * {@link DnsSdDiscoveryListener#onFailure} service discovery is not active and
555     * application does not need to stop it.
556     *
557     * @param c is the channel created at {@link #initialize}
558     * @param listener notifies success or failure. Can be null.
559     */
560    public void stopServiceDiscovery(Channel c, ActionListener listener) {
561        checkChannel(c);
562        c.mDnsSdStopDiscoveryListener = listener;
563        c.mAsyncChannel.sendMessage(STOP_DISCOVERY);
564    }
565
566    /**
567     * Resolve a discovered service. An application can resolve a service right before
568     * establishing a connection to fetch the IP and port details on which to setup
569     * the connection.
570     *
571     * @param c is the channel created at {@link #initialize}
572     * @param serviceName of the the service
573     * @param serviceType of the service
574     * @param listener to receive callback upon success or failure. Cannot be null.
575     */
576    public void resolveService(Channel c, String serviceName, String serviceType,
577            DnsSdResolveListener listener) {
578        checkChannel(c);
579        if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(serviceType)) {
580            throw new IllegalArgumentException("Service name or type cannot be empty");
581        }
582        if (listener == null) throw new
583                IllegalStateException("Resolve listener cannot be null");
584        c.mDnsSdResolveListener = listener;
585        DnsSdServiceInfo serviceInfo = new DnsSdServiceInfo(serviceName, serviceType, null);
586        c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, serviceInfo);
587    }
588
589    /** @hide */
590    public void stopServiceResolve(Channel c) {
591        checkChannel(c);
592        if (c.mDnsSdResolveListener == null) throw new
593                IllegalStateException("Resolve listener needs to be set first");
594        c.mAsyncChannel.sendMessage(STOP_RESOLVE);
595    }
596
597    /**
598     * Get a reference to NetworkService handler. This is used to establish
599     * an AsyncChannel communication with the service
600     *
601     * @return Messenger pointing to the NetworkService handler
602     */
603    private Messenger getMessenger() {
604        try {
605            return mService.getMessenger();
606        } catch (RemoteException e) {
607            return null;
608        }
609    }
610}
611