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