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