Home | History | Annotate | Download | only in connect-devices-wirelessly
      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&trade;. 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 &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
     51     package="com.example.android.nsdchat"
     52     ...
     53 
     54     &lt;uses-permission
     55         android:required="true"
     56         android:name="android.permission.ACCESS_WIFI_STATE"/&gt;
     57     &lt;uses-permission
     58         android:required="true"
     59         android:name="android.permission.CHANGE_WIFI_STATE"/&gt;
     60     &lt;uses-permission
     61         android:required="true"
     62         android:name="android.permission.INTERNET"/&gt;
     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             &#64;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             &#64;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&lt;String, String&gt; buddies = new HashMap&lt;String, String&gt;();
    127 ...
    128 private void discoverService() {
    129     DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
    130         &#64;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         &#64;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                     &#64;Override
    203                     public void onSuccess() {
    204                         // Success!
    205                     }
    206 
    207                     &#64;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             &#64;Override
    222             public void onSuccess() {
    223                 // Success!
    224             }
    225 
    226             &#64;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>
    253