1page.title=Using Network Service Discovery
2
3trainingnavtop=true
4
5@jd:body
6
7<div id="tb-wrapper">
8<div id="tb">
9
10<!-- table of contents -->
11<h2>This lesson teaches you how to</h2>
12<ol>
13  <li><a href="#register">Register Your Service on the Network</a></li>
14  <li><a href="#discover">Discover Services on the Network</a></li>
15  <li><a href="#connect">Connect to Services on the Network</a></li>
16  <li><a href="#teardown">Unregister Your Service on Application Close</a></li>
17</ol>
18
19<!--
20<h2>You should also read</h2>
21    <ul>
22    </ul>
23-->
24<h2>Try it out</h2>
25
26<div class="download-box">
27  <a href="{@docRoot}shareables/training/NsdChat.zip" class="button">Download
28    the sample app</a>
29  <p class="filename">NsdChat.zip</p>
30</div>
31</p>
32
33</div>
34</div>
35
36<p>Adding Network Service Discovery (NSD) to your app allows your users to
37identify other devices on the local network that support the services your app
38requests. This is useful for a variety of peer-to-peer applications such as file
39sharing or multi-player gaming. Android's NSD APIs simplify the effort required
40for you to implement such features.</p>
41
42<p>This lesson shows you how to build an application that can broadcast its
43name and connection information to the local network and scan for information
44from other applications doing the same.  Finally, this lesson shows you how
45to connect to the same application running on another device.</p>
46
47<h2 id="register">Register Your Service on the Network</h2>
48
49<p class="note"><strong>Note: </strong>This step is optional.  If
50you don't care about broadcasting your app's services over the local network,
51you can skip forward to the
52next section, <a href="#discover">Discover Services on the Network</a>.</p>
53
54<p>To register your service on the local network, first create a {@link
55android.net.nsd.NsdServiceInfo} object.  This object provides the information
56that other devices on the network use when they're deciding whether to connect to your
57service. </p>
58
59<pre>
60public void registerService(int port) {
61    // Create the NsdServiceInfo object, and populate it.
62    NsdServiceInfo serviceInfo  = new NsdServiceInfo();
63
64    // The name is subject to change based on conflicts
65    // with other services advertised on the same network.
66    serviceInfo.setServiceName("NsdChat");
67    serviceInfo.setServiceType("_http._tcp.");
68    serviceInfo.setPort(port);
69    ....
70}
71</pre>
72
73<p>This code snippet sets the service name to "NsdChat".
74The name is visible to any device on the network that is using NSD to look for
75local services.  Keep in mind that the name must be unique for any service on the
76network, and Android automatically handles conflict resolution.  If
77two devices on the network both have the NsdChat application installed, one of
78them changes the service name automatically, to something like "NsdChat
79(1)".</p>
80
81<p>The second parameter sets the service type, specifies which protocol and transport
82layer the application uses.  The syntax is
83"_&lt;protocol&gt;._&lt;transportlayer&gt;".  In the
84code snippet, the service uses HTTP protocol running over TCP.  An application
85offering a printer service (for instance, a network printer) would set the
86service type to "_ipp._tcp".</p>
87
88<p class="note"><strong>Note: </strong> The International Assigned Numbers
89Authority (IANA) manages a centralized,
90authoritative list of service types used by service discovery protocols such as NSD and Bonjour.
91You can download the list from <a
92  href="http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml">the
93IANA list of service names and port numbers</a>.
94If you intend to use a new service type, you should reserve it by filling out
95the  <a
96  href="http://www.iana.org/form/ports-services">IANA Ports and Service
97  registration form</a>.</p>
98
99<p>When setting the port for your service, avoid hardcoding it as this
100conflicts with other applications.  For instance, assuming
101that your application always uses port 1337 puts it in potential conflict with
102other installed applications that use the same port.  Instead, use the device's
103next available port.  Because this information is provided to other apps by a
104service broadcast, there's no need for the port your application uses to be
105known by other applications at compile-time.  Instead, the applications can get
106this information from your service broadcast, right before connecting to your
107service.</p>
108
109<p>If you're working with sockets, here's how you can initialize a socket to any
110available port simply by setting it to 0.</p>
111
112<pre>
113public void initializeServerSocket() {
114    // Initialize a server socket on the next available port.
115    mServerSocket = new ServerSocket(0);
116
117    // Store the chosen port.
118    mLocalPort =  mServerSocket.getLocalPort();
119    ...
120}
121</pre>
122
123<p>Now that you've defined the {@link android.net.nsd.NsdServiceInfo
124NsdServiceInfo} object, you need to implement the {@link
125android.net.nsd.NsdManager.RegistrationListener RegistrationListener} interface.  This
126interface contains callbacks used by Android to alert your application of the
127success or failure of service registration and unregistration.
128</p>
129<pre>
130public void initializeRegistrationListener() {
131    mRegistrationListener = new NsdManager.RegistrationListener() {
132
133        &#64;Override
134        public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
135            // Save the service name.  Android may have changed it in order to
136            // resolve a conflict, so update the name you initially requested
137            // with the name Android actually used.
138            mServiceName = NsdServiceInfo.getServiceName();
139        }
140
141        &#64;Override
142        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
143            // Registration failed!  Put debugging code here to determine why.
144        }
145
146        &#64;Override
147        public void onServiceUnregistered(NsdServiceInfo arg0) {
148            // Service has been unregistered.  This only happens when you call
149            // NsdManager.unregisterService() and pass in this listener.
150        }
151
152        &#64;Override
153        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
154            // Unregistration failed.  Put debugging code here to determine why.
155        }
156    };
157}
158</pre>
159
160<p>Now you have all the pieces to register your service.  Call the method
161{@link android.net.nsd.NsdManager#registerService registerService()}.
162</p>
163
164<p>Note that this method is asynchronous, so any code that needs to run
165after the service has been registered must go in the {@link
166android.net.nsd.NsdManager.RegistrationListener#onServiceRegistered(NsdServiceInfo)
167onServiceRegistered()} method.</p>
168
169<pre>
170public void registerService(int port) {
171    NsdServiceInfo serviceInfo  = new NsdServiceInfo();
172    serviceInfo.setServiceName("NsdChat");
173    serviceInfo.setServiceType("_http._tcp.");
174    serviceInfo.setPort(port);
175
176    mNsdManager = Context.getSystemService(Context.NSD_SERVICE);
177
178    mNsdManager.registerService(
179            serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
180}
181</pre>
182
183<h2 id="discover">Discover Services on the Network</h2>
184<p>The network is teeming with life, from the beastly network printers to the
185docile network webcams, to the brutal, fiery battles of nearby tic-tac-toe
186players.  The key to letting your application see this vibrant ecosystem of
187functionality is service discovery.  Your application needs to listen to service
188broadcasts on the network to see what services are available, and filter out
189anything the application can't work with.</p>
190
191<p>Service discovery, like service registration, has two steps:
192 setting up a discovery listener with the relevant callbacks, and making a single asynchronous
193API call to {@link android.net.nsd.NsdManager#discoverServices(String
194, int , NsdManager.DiscoveryListener) discoverServices()}.</p>
195
196<p>First, instantiate an anonymous class that implements {@link
197android.net.nsd.NsdManager.DiscoveryListener}.  The following snippet shows a
198simple example:</p>
199
200<pre>
201public void initializeDiscoveryListener() {
202
203    // Instantiate a new DiscoveryListener
204    mDiscoveryListener = new NsdManager.DiscoveryListener() {
205
206        //  Called as soon as service discovery begins.
207        &#64;Override
208        public void onDiscoveryStarted(String regType) {
209            Log.d(TAG, "Service discovery started");
210        }
211
212        &#64;Override
213        public void onServiceFound(NsdServiceInfo service) {
214            // A service was found!  Do something with it.
215            Log.d(TAG, "Service discovery success" + service);
216            if (!service.getServiceType().equals(SERVICE_TYPE)) {
217                // Service type is the string containing the protocol and
218                // transport layer for this service.
219                Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
220            } else if (service.getServiceName().equals(mServiceName)) {
221                // The name of the service tells the user what they'd be
222                // connecting to. It could be "Bob's Chat App".
223                Log.d(TAG, "Same machine: " + mServiceName);
224            } else if (service.getServiceName().contains("NsdChat")){
225                mNsdManager.resolveService(service, mResolveListener);
226            }
227        }
228
229        &#64;Override
230        public void onServiceLost(NsdServiceInfo service) {
231            // When the network service is no longer available.
232            // Internal bookkeeping code goes here.
233            Log.e(TAG, "service lost" + service);
234        }
235
236        &#64;Override
237        public void onDiscoveryStopped(String serviceType) {
238            Log.i(TAG, "Discovery stopped: " + serviceType);
239        }
240
241        &#64;Override
242        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
243            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
244            mNsdManager.stopServiceDiscovery(this);
245        }
246
247        &#64;Override
248        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
249            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
250            mNsdManager.stopServiceDiscovery(this);
251        }
252    };
253}
254</pre>
255
256<p>The NSD API uses the methods in this interface to inform your application when discovery
257is started, when it fails, and when services are found and lost (lost means "is
258no longer available").  Notice that this snippet does several checks
259when a service is found.</p>
260<ol>
261  <li>The service name of the found service is compared to the service
262name of the local service to determine if the device just picked up its own
263broadcast (which is valid).</li>
264<li>The service type is checked, to verify it's a type of service your
265application can connect to.</li>
266<li>The service name is checked to verify connection to the correct
267application.</li>
268</ol>
269
270<p>Checking the service name isn't always necessary, and is only relevant if you
271want to connect to a specific application.  For instance, the application might
272only want to connect to instances of itself running on other devices.  However, if the
273application wants to connect to a network printer, it's enough to see that the service type
274is "_ipp._tcp".</p>
275
276<p>After setting up the listener, call {@link android.net.nsd.NsdManager#discoverServices(String, int,
277NsdManager.DiscoveryListener) discoverServices()}, passing in the service type
278your application should look for, the discovery protocol to use, and the
279listener you just created.</p>
280
281<pre>
282    mNsdManager.discoverServices(
283        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
284</pre>
285
286
287<h2 id="connect">Connect to Services on the Network</h2>
288<p>When your application finds a service on the network to connect to, it
289must first determine the connection information for that service, using the
290{@link android.net.nsd.NsdManager#resolveService resolveService()} method.
291Implement a {@link android.net.nsd.NsdManager.ResolveListener} to pass into this
292method, and use it to get a {@link android.net.nsd.NsdServiceInfo} containing
293the connection information.</p>
294
295<pre>
296public void initializeResolveListener() {
297    mResolveListener = new NsdManager.ResolveListener() {
298
299        &#64;Override
300        public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
301            // Called when the resolve fails.  Use the error code to debug.
302            Log.e(TAG, "Resolve failed" + errorCode);
303        }
304
305        &#64;Override
306        public void onServiceResolved(NsdServiceInfo serviceInfo) {
307            Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
308
309            if (serviceInfo.getServiceName().equals(mServiceName)) {
310                Log.d(TAG, "Same IP.");
311                return;
312            }
313            mService = serviceInfo;
314            int port = mService.getPort();
315            InetAddress host = mService.getHost();
316        }
317    };
318}
319</pre>
320
321<p>Once the service is resolved, your application receives detailed
322service information including an IP address and port number.  This is  everything
323you need to create your own network connection to the service.</p>
324
325
326<h2 id="teardown">Unregister Your Service on Application Close</h2>
327<p>It's important to enable and disable NSD
328functionality as appropriate during the application's
329lifecycle.  Unregistering your application when it closes down helps prevent
330other applications from thinking it's still active and attempting to connect to
331it.  Also, service discovery is an expensive operation, and should be stopped
332when the parent Activity is paused, and re-enabled when the Activity is
333resumed.  Override the lifecycle methods of your main Activity and insert code
334to start and stop service broadcast and discovery as appropriate.</p>
335
336<pre>
337//In your application's Activity
338
339    &#64;Override
340    protected void onPause() {
341        if (mNsdHelper != null) {
342            mNsdHelper.tearDown();
343        }
344        super.onPause();
345    }
346
347    &#64;Override
348    protected void onResume() {
349        super.onResume();
350        if (mNsdHelper != null) {
351            mNsdHelper.registerService(mConnection.getLocalPort());
352            mNsdHelper.discoverServices();
353        }
354    }
355
356    &#64;Override
357    protected void onDestroy() {
358        mNsdHelper.tearDown();
359        mConnection.tearDown();
360        super.onDestroy();
361    }
362
363    // NsdHelper's tearDown method
364        public void tearDown() {
365        mNsdManager.unregisterService(mRegistrationListener);
366        mNsdManager.stopServiceDiscovery(mDiscoveryListener);
367    }
368</pre>
369
370