1 page.title=Using Network Service Discovery 2 parent.title=Connecting Devices Wirelessly 3 parent.link=index.html 4 5 trainingnavtop=true 6 next.title=Connecting with Wi-Fi Direct 7 next.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 41 identify other devices on the local network that support the services your app 42 requests. This is useful for a variety of peer-to-peer applications such as file 43 sharing or multi-player gaming. Android's NSD APIs simplify the effort required 44 for you to implement such features.</p> 45 46 <p>This lesson shows you how to build an application that can broadcast its 47 name and connection information to the local network and scan for information 48 from other applications doing the same. Finally, this lesson shows you how 49 to 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 54 you don't care about broadcasting your app's services over the local network, 55 you can skip forward to the 56 next 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 59 android.net.nsd.NsdServiceInfo} object. This object provides the information 60 that other devices on the network use when they're deciding whether to connect to your 61 service. </p> 62 63 <pre> 64 public 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". 78 The name is visible to any device on the network that is using NSD to look for 79 local services. Keep in mind that the name must be unique for any service on the 80 network, and Android automatically handles conflict resolution. If 81 two devices on the network both have the NsdChat application installed, one of 82 them 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 86 layer the application uses. The syntax is 87 "_<protocol>._<transportlayer>". In the 88 code snippet, the service uses HTTP protocol running over TCP. An application 89 offering a printer service (for instance, a network printer) would set the 90 service type to "_ipp._tcp".</p> 91 92 <p class="note"><strong>Note: </strong> The International Assigned Numbers 93 Authority (IANA) manages a centralized, 94 authoritative list of service types used by service discovery protocols such as NSD and Bonjour. 95 You can download the list from <a 96 href="http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml">the 97 IANA list of service names and port numbers</a>. 98 If you intend to use a new service type, you should reserve it by filling out 99 the <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 104 conflicts with other applications. For instance, assuming 105 that your application always uses port 1337 puts it in potential conflict with 106 other installed applications that use the same port. Instead, use the device's 107 next available port. Because this information is provided to other apps by a 108 service broadcast, there's no need for the port your application uses to be 109 known by other applications at compile-time. Instead, the applications can get 110 this information from your service broadcast, right before connecting to your 111 service.</p> 112 113 <p>If you're working with sockets, here's how you can initialize a socket to any 114 available port simply by setting it to 0.</p> 115 116 <pre> 117 public 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 128 NsdServiceInfo} object, you need to implement the {@link 129 android.net.nsd.NsdManager.RegistrationListener RegistrationListener} interface. This 130 interface contains callbacks used by Android to alert your application of the 131 success or failure of service registration and unregistration. 132 </p> 133 <pre> 134 public 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 169 after the service has been registered must go in the {@link 170 android.net.nsd.NsdManager.RegistrationListener#onServiceRegistered(NsdServiceInfo) 171 onServiceRegistered()} method.</p> 172 173 <pre> 174 public 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 189 docile network webcams, to the brutal, fiery battles of nearby tic-tac-toe 190 players. The key to letting your application see this vibrant ecosystem of 191 functionality is service discovery. Your application needs to listen to service 192 broadcasts on the network to see what services are available, and filter out 193 anything 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 197 API 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 201 android.net.nsd.NsdManager.DiscoveryListener}. The following snippet shows a 202 simple example:</p> 203 204 <pre> 205 public 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 261 is started, when it fails, and when services are found and lost (lost means "is 262 no longer available"). Notice that this snippet does several checks 263 when a service is found.</p> 264 <ol> 265 <li>The service name of the found service is compared to the service 266 name of the local service to determine if the device just picked up its own 267 broadcast (which is valid).</li> 268 <li>The service type is checked, to verify it's a type of service your 269 application can connect to.</li> 270 <li>The service name is checked to verify connection to the correct 271 application.</li> 272 </ol> 273 274 <p>Checking the service name isn't always necessary, and is only relevant if you 275 want to connect to a specific application. For instance, the application might 276 only want to connect to instances of itself running on other devices. However, if the 277 application wants to connect to a network printer, it's enough to see that the service type 278 is "_ipp._tcp".</p> 279 280 <p>After setting up the listener, call {@link android.net.nsd.NsdManager#discoverServices(String, int, 281 NsdManager.DiscoveryListener) discoverServices()}, passing in the service type 282 your application should look for, the discovery protocol to use, and the 283 listener 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 293 must first determine the connection information for that service, using the 294 {@link android.net.nsd.NsdManager#resolveService resolveService()} method. 295 Implement a {@link android.net.nsd.NsdManager.ResolveListener} to pass into this 296 method, and use it to get a {@link android.net.nsd.NsdServiceInfo} containing 297 the connection information.</p> 298 299 <pre> 300 public 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 326 service information including an IP address and port number. This is everything 327 you 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 332 functionality as appropriate during the application's 333 lifecycle. Unregistering your application when it closes down helps prevent 334 other applications from thinking it's still active and attempting to connect to 335 it. Also, service discovery is an expensive operation, and should be stopped 336 when the parent Activity is paused, and re-enabled when the Activity is 337 resumed. Override the lifecycle methods of your main Activity and insert code 338 to 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