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"_<protocol>._<transportlayer>". 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 @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 @Override 146 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 147 // Registration failed! Put debugging code here to determine why. 148 } 149 150 @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 @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 @Override 212 public void onDiscoveryStarted(String regType) { 213 Log.d(TAG, "Service discovery started"); 214 } 215 216 @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 @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 @Override 241 public void onDiscoveryStopped(String serviceType) { 242 Log.i(TAG, "Discovery stopped: " + serviceType); 243 } 244 245 @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 @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 @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 @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 @Override 344 protected void onPause() { 345 if (mNsdHelper != null) { 346 mNsdHelper.tearDown(); 347 } 348 super.onPause(); 349 } 350 351 @Override 352 protected void onResume() { 353 super.onResume(); 354 if (mNsdHelper != null) { 355 mNsdHelper.registerService(mConnection.getLocalPort()); 356 mNsdHelper.discoverServices(); 357 } 358 } 359 360 @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