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&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
48    package="com.example.android.nsdchat"
49    ...
50
51    &lt;uses-permission
52        android:required="true"
53        android:name="android.permission.ACCESS_WIFI_STATE"/&gt;
54    &lt;uses-permission
55        android:required="true"
56        android:name="android.permission.CHANGE_WIFI_STATE"/&gt;
57    &lt;uses-permission
58        android:required="true"
59        android:name="android.permission.INTERNET"/&gt;
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            &#64;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            &#64;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&lt;String, String&gt; buddies = new HashMap&lt;String, String&gt;();
124...
125private void discoverService() {
126    DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
127        &#64;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        &#64;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                    &#64;Override
200                    public void onSuccess() {
201                        // Success!
202                    }
203
204                    &#64;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            &#64;Override
219            public void onSuccess() {
220                // Success!
221            }
222
223            &#64;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