1 page.title=Using Wi-Fi P2P for Service Discovery 2 trainingnavtop=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 25 how to discover services that are connected to a local network. However, using 26 Wi-Fi Peer-to-Peer (P2P) Service Discovery allows you to discover the services of nearby devices 27 directly, without being connected to a network. You can also advertise the services 28 running on your device. These capabilities help you communicate between apps, 29 even 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 31 APIs outlined in a previous lesson, implementing them in code is very different. 32 This lesson shows you how to discover services available from other devices, 33 using 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 39 android.Manifest.permission#CHANGE_WIFI_STATE}, {@link 40 android.Manifest.permission#ACCESS_WIFI_STATE}, 41 and {@link android.Manifest.permission#INTERNET} 42 permissions to your manifest. Even though Wi-Fi P2P doesn't require an 43 Internet connection, it uses standard Java sockets, and using these in Android 44 requires 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 65 service discovery. Once your local service is registered, the framework 66 automatically 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 75 android.net.wifi.p2p.WifiP2pManager#addLocalService(WifiP2pManager.Channel, 76 WifiP2pServiceInfo, WifiP2pManager.ActionListener) addLocalService()} to register the local 77 service 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 114 the first thing to do is set those up. Create a {@link 115 android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener} to listen for 116 incoming records. This record can optionally be broadcast by other 117 devices. When one comes in, copy the device address and any other 118 relevant information you want into a data structure external to the current 119 method, so you can access it later. The following example assumes that the 120 record contains a "buddyname" field, populated with the user's identity.</p> 121 122 <pre> 123 final HashMap<String, String> buddies = new HashMap<String, String>(); 124 ... 125 private 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 145 android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener}. This 146 receives the actual description and connection information. The previous code 147 snippet implemented a {@link java.util.Map} object to pair a device address with the buddy 148 name. The service response listener uses this to link the DNS record with the 149 corresponding service information. Once both 150 listeners are implemented, add them to the {@link 151 android.net.wifi.p2p.WifiP2pManager} using the {@link 152 android.net.wifi.p2p.WifiP2pManager#setDnsSdResponseListeners(WifiP2pManager.Channel, 153 WifiP2pManager.DnsSdServiceResponseListener, 154 WifiP2pManager.DnsSdTxtRecordListener) setDnsSdResponseListeners()} method.</p> 155 156 <pre> 157 private 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 190 android.net.wifi.p2p.WifiP2pManager#addServiceRequest(WifiP2pManager.Channel, 191 WifiP2pServiceRequest, WifiP2pManager.ActionListener) addServiceRequest()}. 192 This 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 212 android.net.wifi.p2p.WifiP2pManager#discoverServices(WifiP2pManager.Channel, 213 WifiP2pManager.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 235 that the asynchronous calls you've made take an 236 {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} as an argument, and 237 this provides you with callbacks indicating success or failure. To diagnose 238 problems, put debugging code in {@link 239 android.net.wifi.p2p.WifiP2pManager.ActionListener#onFailure(int) onFailure()}. The error code 240 provided by the method hints at the problem. Here are the possible error values 241 and 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