|
| 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> |
0 commit comments