Home | History | Annotate | Download | only in connect-devices-wirelessly
      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 &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
     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             &#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
    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&lt;String, String&gt; buddies = new HashMap&lt;String, String&gt;();
    124 ...
    125 private 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
    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         &#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
    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                     &#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
    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             &#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
    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