1 page.title=Using Wi-Fi Direct for Service Discovery 2 parent.title=Connecting Devices Wirelessly 3 parent.link=index.html 4 5 trainingnavtop=true 6 7 @jd:body 8 9 <div id="tb-wrapper"> 10 <div id="tb"> 11 <h2>This lesson teaches you to</h2> 12 <ol> 13 <li><a href="#manifest">Set Up the Manifest</a></li> 14 <li><a href="#register">Add a Local Service</a></li> 15 <li><a href="#discover">Discover Nearby Services</a></li> 16 </ol> 17 <!-- 18 <h2>You should also read</h2> 19 <ul> 20 <li><a href="#"></a></li> 21 </ul> 22 --> 23 </div> 24 </div> 25 26 <p>The first lesson in this class, <a href="nsd.html">Using Network Service 27 Discovery</a>, showed you 28 how to discover services that are connected to a local network. However, using 29 Wi-Fi Direct&trad; Service Discovery allows you to discover the services of nearby devices directly, 30 without being connected to a network. You can also advertise the services 31 running on your device. These capabilities help you communicate between apps, 32 even when no local network or hotspot is available.</p> 33 <p>While this set of APIs is similar in purpose to the Network Service Discovery 34 APIs outlined in a previous lesson, implementing them in code is very different. 35 This lesson shows you how to discover services available from other devices, 36 using Wi-Fi Direct™. The lesson assumes that you're already familiar with the 37 <a href="{@docRoot}guide/topics/connectivity/wifip2p.html">Wi-Fi Direct</a> API.</p> 38 39 40 <h2 id="manifest">Set Up the Manifest</h2> 41 <p>In order to use Wi-Fi Direct, add the {@link 42 android.Manifest.permission#CHANGE_WIFI_STATE}, {@link 43 android.Manifest.permission#ACCESS_WIFI_STATE}, 44 and {@link android.Manifest.permission#INTERNET} 45 permissions to your manifest. Even though Wi-Fi Direct doesn't require an 46 Internet connection, it uses standard Java sockets, and using these in Android 47 requires the requested permissions.</p> 48 49 <pre> 50 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 51 package="com.example.android.nsdchat" 52 ... 53 54 <uses-permission 55 android:required="true" 56 android:name="android.permission.ACCESS_WIFI_STATE"/> 57 <uses-permission 58 android:required="true" 59 android:name="android.permission.CHANGE_WIFI_STATE"/> 60 <uses-permission 61 android:required="true" 62 android:name="android.permission.INTERNET"/> 63 ... 64 </pre> 65 66 <h2 id="register">Add a Local Service</h2> 67 <p>If you're providing a local service, you need to register it for 68 service discovery. Once your local service is registered, the framework 69 automatically responds to service discovery requests from peers.</p> 70 71 <p>To create a local service:</p> 72 73 <ol> 74 <li>Create a 75 {@link android.net.wifi.p2p.nsd.WifiP2pServiceInfo} object.</li> 76 <li>Populate it with information about your service.</li> 77 <li>Call {@link 78 android.net.wifi.p2p.WifiP2pManager#addLocalService(WifiP2pManager.Channel, 79 WifiP2pServiceInfo, WifiP2pManager.ActionListener) addLocalService()} to register the local 80 service for service discovery.</li> 81 </ol> 82 83 <pre> 84 private void startRegistration() { 85 // Create a string map containing information about your service. 86 Map<String,String> record = new HashMap<String,String>(); 87 record.put("listenport", String.valueOf(SERVER_PORT)); 88 record.put("buddyname", "John Doe" + (int) (Math.random() * 1000)); 89 record.put("available", "visible"); 90 91 // Service information. Pass it an instance name, service type 92 // _protocol._transportlayer , and the map containing 93 // information other devices will want once they connect to this one. 94 WifiP2pDnsSdServiceInfo serviceInfo = 95 WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record); 96 97 // Add the local service, sending the service info, network channel, 98 // and listener that will be used to indicate success or failure of 99 // the request. 100 mManager.addLocalService(channel, serviceInfo, new ActionListener() { 101 @Override 102 public void onSuccess() { 103 // Command successful! Code isn't necessarily needed here, 104 // Unless you want to update the UI or add logging statements. 105 } 106 107 @Override 108 public void onFailure(int arg0) { 109 // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY 110 } 111 }); 112 } 113 </pre> 114 115 <h2 id="discover">Discover Nearby Services</h2> 116 <p>Android uses callback methods to notify your application of available services, so 117 the first thing to do is set those up. Create a {@link 118 android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener} to listen for 119 incoming records. This record can optionally be broadcast by other 120 devices. When one comes in, copy the device address and any other 121 relevant information you want into a data structure external to the current 122 method, so you can access it later. The following example assumes that the 123 record contains a "buddyname" field, populated with the user's identity.</p> 124 125 <pre> 126 final HashMap<String, String> buddies = new HashMap<String, String>(); 127 ... 128 private void discoverService() { 129 DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() { 130 @Override 131 /* Callback includes: 132 * fullDomain: full domain name: e.g "printer._ipp._tcp.local." 133 * record: TXT record dta as a map of key/value pairs. 134 * device: The device running the advertised service. 135 */ 136 137 public void onDnsSdTxtRecordAvailable( 138 String fullDomain, Map<String,String> record, WifiP2pDevice device) { 139 Log.d(TAG, "DnsSdTxtRecord available -" + record.toString()); 140 buddies.put(device.deviceAddress, record.get("buddyname")); 141 } 142 }; 143 ... 144 } 145 </pre> 146 147 <p>To get the service information, create a {@link 148 android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener}. This 149 receives the actual description and connection information. The previous code 150 snippet implemented a {@link java.util.Map} object to pair a device address with the buddy 151 name. The service response listener uses this to link the DNS record with the 152 corresponding service information. Once both 153 listeners are implemented, add them to the {@link 154 android.net.wifi.p2p.WifiP2pManager} using the {@link 155 android.net.wifi.p2p.WifiP2pManager#setDnsSdResponseListeners(WifiP2pManager.Channel, 156 WifiP2pManager.DnsSdServiceResponseListener, 157 WifiP2pManager.DnsSdTxtRecordListener) setDnsSdResponseListeners()} method.</p> 158 159 <pre> 160 private void discoverService() { 161 ... 162 163 DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() { 164 @Override 165 public void onDnsSdServiceAvailable(String instanceName, String registrationType, 166 WifiP2pDevice resourceType) { 167 168 // Update the device name with the human-friendly version from 169 // the DnsTxtRecord, assuming one arrived. 170 resourceType.deviceName = buddies 171 .containsKey(resourceType.deviceAddress) ? buddies 172 .get(resourceType.deviceAddress) : resourceType.deviceName; 173 174 // Add to the custom adapter defined specifically for showing 175 // wifi devices. 176 WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager() 177 .findFragmentById(R.id.frag_peerlist); 178 WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment 179 .getListAdapter()); 180 181 adapter.add(resourceType); 182 adapter.notifyDataSetChanged(); 183 Log.d(TAG, "onBonjourServiceAvailable " + instanceName); 184 } 185 }; 186 187 mManager.setDnsSdResponseListeners(channel, servListener, txtListener); 188 ... 189 } 190 </pre> 191 192 <p>Now create a service request and call {@link 193 android.net.wifi.p2p.WifiP2pManager#addServiceRequest(WifiP2pManager.Channel, 194 WifiP2pServiceRequest, WifiP2pManager.ActionListener) addServiceRequest()}. 195 This method also takes a listener to report success or failure.</p> 196 197 <pre> 198 serviceRequest = WifiP2pDnsSdServiceRequest.newInstance(); 199 mManager.addServiceRequest(channel, 200 serviceRequest, 201 new ActionListener() { 202 @Override 203 public void onSuccess() { 204 // Success! 205 } 206 207 @Override 208 public void onFailure(int code) { 209 // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY 210 } 211 }); 212 </pre> 213 214 <p>Finally, make the call to {@link 215 android.net.wifi.p2p.WifiP2pManager#discoverServices(WifiP2pManager.Channel, 216 WifiP2pManager.ActionListener) discoverServices()}.</p> 217 218 <pre> 219 mManager.discoverServices(channel, new ActionListener() { 220 221 @Override 222 public void onSuccess() { 223 // Success! 224 } 225 226 @Override 227 public void onFailure(int code) { 228 // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY 229 if (code == WifiP2pManager.P2P_UNSUPPORTED) { 230 Log.d(TAG, "P2P isn't supported on this device."); 231 else if(...) 232 ... 233 } 234 }); 235 </pre> 236 237 <p>If all goes well, hooray, you're done! If you encounter problems, remember 238 that the asynchronous calls you've made take an 239 {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} as an argument, and 240 this provides you with callbacks indicating success or failure. To diagnose 241 problems, put debugging code in {@link 242 android.net.wifi.p2p.WifiP2pManager.ActionListener#onFailure(int) onFailure()}. The error code 243 provided by the method hints at the problem. Here are the possible error values 244 and what they mean</p> 245 <dl> 246 <dt> {@link android.net.wifi.p2p.WifiP2pManager#P2P_UNSUPPORTED}</dt> 247 <dd> Wi-Fi Direct isn't supported on the device running the app.</dd> 248 <dt> {@link android.net.wifi.p2p.WifiP2pManager#BUSY}</dt> 249 <dd> The system is to busy to process the request.</dd> 250 <dt> {@link android.net.wifi.p2p.WifiP2pManager#ERROR}</dt> 251 <dd> The operation failed due to an internal error.</dd> 252 </dl> 253