Initial commit of wireless connectivity/discovery class.

Change-Id: Ifbcbb8f5021a749ddb131d678ee1117835168598
diff --git a/docs/html/shareables/training/nsdchat.zip b/docs/html/shareables/training/nsdchat.zip
new file mode 100644
index 0000000..a106975
--- /dev/null
+++ b/docs/html/shareables/training/nsdchat.zip
Binary files differ
diff --git a/docs/html/training/connect-devices-wirelessly/index.jd b/docs/html/training/connect-devices-wirelessly/index.jd
new file mode 100644
index 0000000..37cf633
--- /dev/null
+++ b/docs/html/training/connect-devices-wirelessly/index.jd
@@ -0,0 +1,62 @@
+page.title=Connecting Devices Wirelessly
+
+trainingnavtop=true
+startpage=true
+next.title=Using Network Service Discovery
+next.link=nsd.html
+
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+  <li>Android 4.1 or higher</li>
+</ul>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/topics/connectivity/wifip2p.html">Wi-Fi Direct</a></li>
+</ul>
+
+
+</div>
+</div>
+
+
+<p>Besides enabling communication with the cloud, Android's wireless APIs also
+enable communication with other devices on the same local network, and even
+devices which are not on a network, but are physically nearby.  The addition of
+Network Service Discovery (NSD) takes this further by allowing an application to
+seek out a nearby device running services with which it can communicate.
+Integrating this functionality into your application helps you provide a wide range
+of features, such as playing games with users in the same room, pulling
+images from a networked NSD-enabled webcam, or remotely logging into
+other machines on the same network.</p>
+<p>This class describes the key APIs for finding and
+connecting to other devices from your application.  Specifically, it
+describes the NSD API for discovering available services and the Wi-Fi
+Direct&trade; API for doing peer-to-peer wireless connections.  This class also
+shows you how to use NSD and Wi-Fi Direct in
+combination to detect the services offered by a device and connect to the
+device when neither device is connected to a network.
+</p>
+<h2>Lessons</h2>
+
+<dl>
+  <dt><strong><a href="nsd.html">Using Network Service Discovery</a></strong></dt>
+  <dd>Learn how to broadcast services offered by your own application, discover
+  services offered on the local network, and use NSD to determine the connection
+  details for the service you wish to connect to.</dd>
+  <dt><strong><a href="wifi-direct.html">Connecting with Wi-Fi Direct</a></strong></dt>
+  <dd>Learn how to fetch a list of nearby peer devices, create an access point
+  for legacy devices, and connect to other devices capable of Wi-Fi Direct
+  connections.</dd>
+  <dt><strong><a href="nsd-wifi-direct.html">Using Wi-Fi Direct for Service
+      Discovery</a></strong></dt>
+  <dd>Learn how to discover services published by nearby devices without being
+  on the same network, using Wi-Fi Direct.</dd>
+</dl>
+
diff --git a/docs/html/training/connect-devices-wirelessly/nsd-wifi-direct.jd b/docs/html/training/connect-devices-wirelessly/nsd-wifi-direct.jd
new file mode 100644
index 0000000..5e276de
--- /dev/null
+++ b/docs/html/training/connect-devices-wirelessly/nsd-wifi-direct.jd
@@ -0,0 +1,252 @@
+page.title=Using Wi-Fi Direct for Service Discovery
+parent.title=Connecting Devices Wirelessly
+parent.link=index.html
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+  <div id="tb">
+    <h2>This lesson teaches you to</h2>
+    <ol>
+      <li><a href="#manifest">Set Up the Manifest</a></li>
+      <li><a href="#register">Add a Local Service</a></li>
+      <li><a href="#discover">Discover Nearby Services</a></li>
+    </ol>
+    <!--
+    <h2>You should also read</h2>
+    <ul>
+      <li><a href="#"></a></li>
+    </ul>
+    -->
+  </div>
+</div>
+
+<p>The first lesson in this class, <a href="nsd.html">Using Network Service
+  Discovery</a>, showed you
+how to discover services that are connected to a local network. However, using
+Wi-Fi Direct&trad; Service Discovery allows you to discover the services of nearby devices directly,
+without being connected to a network.  You can also advertise the services
+running on your device.  These capabilities help you communicate between apps,
+even when no local network or hotspot is available.</p>
+<p>While this set of APIs is similar in purpose to the Network Service Discovery
+APIs outlined in a previous lesson, implementing them in code is very different.
+This lesson shows you how to discover services available from other devices,
+using Wi-Fi Direct&trade;. The lesson assumes that you're already familiar with the
+<a href="{@docRoot}guide/topics/connectivity/wifip2p.html">Wi-Fi Direct</a> API.</p>
+
+
+<h2 id="manifest">Set Up the Manifest</h2>
+<p>In order to use Wi-Fi Direct, add the {@link
+android.Manifest.permission#CHANGE_WIFI_STATE}, {@link
+android.Manifest.permission#ACCESS_WIFI_STATE},
+and {@link android.Manifest.permission#INTERNET}
+permissions to your manifest.  Even though Wi-Fi Direct doesn't require an
+Internet connection, it uses standard Java sockets, and using these in Android
+requires the requested permissions.</p>
+
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.nsdchat"
+    ...
+
+    &lt;uses-permission
+        android:required="true"
+        android:name="android.permission.ACCESS_WIFI_STATE"/&gt;
+    &lt;uses-permission
+        android:required="true"
+        android:name="android.permission.CHANGE_WIFI_STATE"/&gt;
+    &lt;uses-permission
+        android:required="true"
+        android:name="android.permission.INTERNET"/&gt;
+    ...
+</pre>
+
+<h2 id="register">Add a Local Service</h2>
+<p>If you're providing a local service, you need to register it for
+service discovery.  Once your local service is registered, the framework
+automatically responds to service discovery requests from peers.</p>
+
+<p>To create a local service:</p>
+
+<ol>
+  <li>Create a
+{@link android.net.wifi.p2p.nsd.WifiP2pServiceInfo} object.</li>
+  <li>Populate it with information about your service.</li>
+  <li>Call {@link
+android.net.wifi.p2p.WifiP2pManager#addLocalService(WifiP2pManager.Channel,
+WifiP2pServiceInfo, WifiP2pManager.ActionListener) addLocalService()} to register the local
+service for service discovery.</li>
+</ol>
+
+<pre>
+     private void startRegistration() {
+        //  Create a string map containing information about your service.
+        Map<String,String> record = new HashMap<String,String>();
+        record.put("listenport", String.valueOf(SERVER_PORT));
+        record.put("buddyname", "John Doe" + (int) (Math.random() * 1000));
+        record.put("available", "visible");
+
+        // Service information.  Pass it an instance name, service type
+        // _protocol._transportlayer , and the map containing
+        // information other devices will want once they connect to this one.
+        WifiP2pDnsSdServiceInfo serviceInfo =
+                WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record);
+
+        // Add the local service, sending the service info, network channel,
+        // and listener that will be used to indicate success or failure of
+        // the request.
+        mManager.addLocalService(channel, serviceInfo, new ActionListener() {
+            &#64;Override
+            public void onSuccess() {
+                // Command successful! Code isn't necessarily needed here,
+                // Unless you want to update the UI or add logging statements.
+            }
+
+            &#64;Override
+            public void onFailure(int arg0) {
+                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
+            }
+        });
+    }
+</pre>
+
+<h2 id="discover">Discover Nearby Services</h2>
+<p>Android uses callback methods to notify your application of available services, so
+the first thing to do is set those up.  Create a {@link
+android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener} to listen for
+incoming records.  This record can optionally be broadcast by other
+devices.  When one comes in, copy the device address and any other
+relevant information you want into a data structure external to the current
+method, so you can access it later.  The following example assumes that the
+record contains a "buddyname" field, populated with the user's identity.</p>
+
+<pre>
+final HashMap&lt;String, String&gt; buddies = new HashMap&lt;String, String&gt;();
+...
+private void discoverService() {
+    DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
+        &#64;Override
+        /* Callback includes:
+         * fullDomain: full domain name: e.g "printer._ipp._tcp.local."
+         * record: TXT record dta as a map of key/value pairs.
+         * device: The device running the advertised service.
+         */
+
+        public void onDnsSdTxtRecordAvailable(
+                String fullDomain, Map<String,String> record, WifiP2pDevice device) {
+                Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
+                buddies.put(device.deviceAddress, record.get("buddyname"));
+            }
+        };
+    ...
+}
+</pre>
+
+<p>To get the service information, create a {@link
+android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener}.  This
+receives the actual description and connection information.  The previous code
+snippet implemented a {@link java.util.Map} object to pair a device address with the buddy
+name.  The service response listener uses this to link the DNS record with the
+corresponding service information.  Once both
+listeners are implemented, add them to the {@link
+android.net.wifi.p2p.WifiP2pManager} using the {@link
+android.net.wifi.p2p.WifiP2pManager#setDnsSdResponseListeners(WifiP2pManager.Channel,
+WifiP2pManager.DnsSdServiceResponseListener,
+WifiP2pManager.DnsSdTxtRecordListener) setDnsSdResponseListeners()} method.</p>
+
+<pre>
+private void discoverService() {
+...
+
+    DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
+        &#64;Override
+        public void onDnsSdServiceAvailable(String instanceName, String registrationType,
+                WifiP2pDevice resourceType) {
+
+                // Update the device name with the human-friendly version from
+                // the DnsTxtRecord, assuming one arrived.
+                resourceType.deviceName = buddies
+                        .containsKey(resourceType.deviceAddress) ? buddies
+                        .get(resourceType.deviceAddress) : resourceType.deviceName;
+
+                // Add to the custom adapter defined specifically for showing
+                // wifi devices.
+                WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
+                        .findFragmentById(R.id.frag_peerlist);
+                WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
+                        .getListAdapter());
+
+                adapter.add(resourceType);
+                adapter.notifyDataSetChanged();
+                Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
+        }
+    };
+
+    mManager.setDnsSdResponseListeners(channel, servListener, txtListener);
+    ...
+}
+</pre>
+
+<p>Now create a service request and call {@link
+android.net.wifi.p2p.WifiP2pManager#addServiceRequest(WifiP2pManager.Channel,
+WifiP2pServiceRequest, WifiP2pManager.ActionListener) addServiceRequest()}.
+This method also takes a listener to report success or failure.</p>
+
+<pre>
+        serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
+        mManager.addServiceRequest(channel,
+                serviceRequest,
+                new ActionListener() {
+                    &#64;Override
+                    public void onSuccess() {
+                        // Success!
+                    }
+
+                    &#64;Override
+                    public void onFailure(int code) {
+                        // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
+                    }
+                });
+</pre>
+
+<p>Finally, make the call to {@link
+android.net.wifi.p2p.WifiP2pManager#discoverServices(WifiP2pManager.Channel,
+WifiP2pManager.ActionListener) discoverServices()}.</p>
+
+<pre>
+        mManager.discoverServices(channel, new ActionListener() {
+
+            &#64;Override
+            public void onSuccess() {
+                // Success!
+            }
+
+            &#64;Override
+            public void onFailure(int code) {
+                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
+                if (code == WifiP2pManager.P2P_UNSUPPORTED) {
+                    Log.d(TAG, "P2P isn't supported on this device.");
+                else if(...)
+                    ...
+            }
+        });
+</pre>
+
+<p>If all goes well, hooray, you're done!  If you encounter problems, remember
+that the asynchronous calls you've made take an
+{@link android.net.wifi.p2p.WifiP2pManager.ActionListener} as an argument, and
+this provides you with callbacks indicating success or failure.  To diagnose
+problems, put debugging code in {@link
+android.net.wifi.p2p.WifiP2pManager.ActionListener#onFailure(int) onFailure()}.  The error code
+provided by the method hints at the problem.  Here are the possible error values
+and what they mean</p>
+<dl>
+  <dt> {@link android.net.wifi.p2p.WifiP2pManager#P2P_UNSUPPORTED}</dt>
+  <dd> Wi-Fi Direct isn't supported on the device running the app.</dd>
+  <dt> {@link android.net.wifi.p2p.WifiP2pManager#BUSY}</dt>
+  <dd> The system is to busy to process the request.</dd>
+  <dt> {@link android.net.wifi.p2p.WifiP2pManager#ERROR}</dt>
+  <dd> The operation failed due to an internal error.</dd>
+</dl>
diff --git a/docs/html/training/connect-devices-wirelessly/nsd.jd b/docs/html/training/connect-devices-wirelessly/nsd.jd
new file mode 100644
index 0000000..d2b01a1
--- /dev/null
+++ b/docs/html/training/connect-devices-wirelessly/nsd.jd
@@ -0,0 +1,373 @@
+page.title=Using Network Service Discovery
+parent.title=Connecting Devices Wirelessly
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Connecting with Wi-Fi Direct
+next.link=wifi-direct.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you how to</h2>
+<ol>
+  <li><a href="#register">Register Your Service on the Network</a></li>
+  <li><a href="#discover">Discover Services on the Network</a></li>
+  <li><a href="#connect">Connect to Services on the Network</a></li>
+  <li><a href="#teardown">Unregister Your Service on Application Close</a></li>
+</ol>
+
+<!--
+<h2>You should also read</h2>
+    <ul>
+    </ul>
+-->
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/nsdchat.zip" class="button">Download
+    the sample app</a>
+  <p class="filename">nsdchat.zip</p>
+</div>
+</p>
+
+</div>
+</div>
+
+<p>Adding Network Service Discovery (NSD) to your app allows your users to
+identify other devices on the local network that support the services your app
+requests. This is useful for a variety of peer-to-peer applications such as file
+sharing or multi-player gaming. Android's NSD APIs simplify the effort required
+for you to implement such features.</p>
+
+<p>This lesson shows you how to build an application that can broadcast its
+name and connection information to the local network and scan for information
+from other applications doing the same.  Finally, this lesson shows you how
+to connect to the same application running on another device.</p>
+
+<h2 id="register">Register Your Service on the Network</h2>
+
+<p class="note"><strong>Note: </strong>This step is optional.  If
+you don't care about broadcasting your app's services over the local network,
+you can skip forward to the
+next section, <a href="#discover">Discover Services on the Network</a>.</p>
+
+<p>To register your service on the local network, first create a {@link
+android.net.nsd.NsdServiceInfo} object.  This object provides the information
+that other devices on the network use when they're deciding whether to connect to your
+service. </p>
+
+<pre>
+public void registerService(int port) {
+    // Create the NsdServiceInfo object, and populate it.
+    NsdServiceInfo serviceInfo  = new NsdServiceInfo();
+
+    // The name is subject to change based on conflicts
+    // with other services advertised on the same network.
+    serviceInfo.setServiceName("NsdChat");
+    serviceInfo.setServiceType("_http._tcp.");
+    serviceInfo.setPort(port);
+    ....
+}
+</pre>
+
+<p>This code snippet sets the service name to "NsdChat".
+The name is visible to any device on the network that is using NSD to look for
+local services.  Keep in mind that the name must be unique for any service on the
+network, and Android automatically handles conflict resolution.  If
+two devices on the network both have the NsdChat application installed, one of
+them changes the service name automatically, to something like "NsdChat
+(1)".</p>
+
+<p>The second parameter sets the service type, specifies which protocol and transport
+layer the application uses.  The syntax is
+"_&lt;protocol&gt;._&lt;transportlayer&gt;".  In the
+code snippet, the service uses HTTP protocol running over TCP.  An application
+offering a printer service (for instance, a network printer) would set the
+service type to "_ipp._tcp".</p>
+
+<p class="note"><strong>Note: </strong> The International Assigned Numbers
+Authority (IANA) manages a centralized,
+authoritative list of service types used by service discovery protocols such as NSD and Bonjour.
+You can download the list from <a
+  href="http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml">the
+IANA list of service names and port numbers</a>.
+If you intend to use a new service type, you should reserve it by filling out
+the  <a
+  href="http://www.iana.org/form/ports-services">IANA Ports and Service
+  registration form</a>.</p>
+
+<p>When setting the port for your service, avoid hardcoding it as this
+conflicts with other applications.  For instance, assuming
+that your application always uses port 1337 puts it in potential conflict with
+other installed applications that use the same port.  Instead, use the device's
+next available port.  Because this information is provided to other apps by a
+service broadcast, there's no need for the port your application uses to be
+known by other applications at compile-time.  Instead, the applications can get
+this information from your service broadcast, right before connecting to your
+service.</p>
+
+<p>If you're working with sockets, here's how you can initialize a socket to any
+available port simply by setting it to 0.</p>
+
+<pre>
+public void initializeServerSocket() {
+    // Initialize a server socket on the next available port.
+    mServerSocket = new ServerSocket(0);
+
+    // Store the chosen port.
+    mLocalPort =  mServerSocket.getLocalPort();
+    ...
+}
+</pre>
+
+<p>Now that you've defined the {@link android.net.nsd.NsdServiceInfo
+NsdServiceInfo} object, you need to implement the {@link
+android.net.nsd.NsdManager.RegistrationListener RegistrationListener} interface.  This
+interface contains callbacks used by Android to alert your application of the
+success or failure of service registration and unregistration.
+</p>
+<pre>
+public void initializeRegistrationListener() {
+    mRegistrationListener = new NsdManager.RegistrationListener() {
+
+        &#64;Override
+        public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
+            // Save the service name.  Android may have changed it in order to
+            // resolve a conflict, so update the name you initially requested
+            // with the name Android actually used.
+            mServiceName = NsdServiceInfo.getServiceName();
+        }
+
+        &#64;Override
+        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            // Registration failed!  Put debugging code here to determine why.
+        }
+
+        &#64;Override
+        public void onServiceUnregistered(NsdServiceInfo arg0) {
+            // Service has been unregistered.  This only happens when you call
+            // NsdManager.unregisterService() and pass in this listener.
+        }
+
+        &#64;Override
+        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            // Unregistration failed.  Put debugging code here to determine why.
+        }
+    };
+}
+</pre>
+
+<p>Now you have all the pieces to register your service.  Call the method
+{@link android.net.nsd.NsdManager#registerService registerService()}.
+</p>
+
+<p>Note that this method is asynchronous, so any code that needs to run
+after the service has been registered must go in the {@link
+android.net.nsd.NsdManager.RegistrationListener#onServiceRegistered(NsdServiceInfo)
+onServiceRegistered()} method.</p>
+
+<pre>
+public void registerService(int port) {
+    NsdServiceInfo serviceInfo  = new NsdServiceInfo();
+    serviceInfo.setServiceName("NsdChat");
+    serviceInfo.setServiceType("_http._tcp.");
+    serviceInfo.setPort(port);
+
+    mNsdManager = Context.getSystemService(Context.NSD_SERVICE);
+
+    mNsdManager.registerService(
+            serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
+}
+</pre>
+
+<h2 id="discover">Discover Services on the Network</h2>
+<p>The network is teeming with life, from the beastly network printers to the
+docile network webcams, to the brutal, fiery battles of nearby tic-tac-toe
+players.  The key to letting your application see this vibrant ecosystem of
+functionality is service discovery.  Your application needs to listen to service
+broadcasts on the network to see what services are available, and filter out
+anything the application can't work with.</p>
+
+<p>Service discovery, like service registration, has two steps:
+ setting up a discovery listener with the relevant callbacks, and making a single asynchronous
+API call to {@link android.net.nsd.NsdManager#discoverServices(String
+, int , NsdManager.DiscoveryListener) discoverServices()}.</p>
+
+<p>First, instantiate an anonymous class that implements {@link
+android.net.nsd.NsdManager.DiscoveryListener}.  The following snippet shows a
+simple example:</p>
+
+<pre>
+public void initializeDiscoveryListener() {
+
+    // Instantiate a new DiscoveryListener
+    mDiscoveryListener = new NsdManager.DiscoveryListener() {
+
+        //  Called as soon as service discovery begins.
+        &#64;Override
+        public void onDiscoveryStarted(String regType) {
+            Log.d(TAG, "Service discovery started");
+        }
+
+        &#64;Override
+        public void onServiceFound(NsdServiceInfo service) {
+            // A service was found!  Do something with it.
+            Log.d(TAG, "Service discovery success" + service);
+            if (!service.getServiceType().equals(SERVICE_TYPE)) {
+                // Service type is the string containing the protocol and
+                // transport layer for this service.
+                Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
+            } else if (service.getServiceName().equals(mServiceName)) {
+                // The name of the service tells the user what they'd be
+                // connecting to. It could be "Bob's Chat App".
+                Log.d(TAG, "Same machine: " + mServiceName);
+            } else if (service.getServiceName().contains("NsdChat")){
+                mNsdManager.resolveService(service, mResolveListener);
+            }
+        }
+
+        &#64;Override
+        public void onServiceLost(NsdServiceInfo service) {
+            // When the network service is no longer available.
+            // Internal bookkeeping code goes here.
+            Log.e(TAG, "service lost" + service);
+        }
+
+        &#64;Override
+        public void onDiscoveryStopped(String serviceType) {
+            Log.i(TAG, "Discovery stopped: " + serviceType);
+        }
+
+        &#64;Override
+        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
+            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
+            mNsdManager.stopServiceDiscovery(this);
+        }
+
+        &#64;Override
+        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
+            mNsdManager.stopServiceDiscovery(this);
+        }
+    };
+}
+</pre>
+
+<p>The NSD API uses the methods in this interface to inform your application when discovery
+is started, when it fails, and when services are found and lost (lost means "is
+no longer available").  Notice that this snippet does several checks
+when a service is found.</p>
+<ol>
+  <li>The service name of the found service is compared to the service
+name of the local service to determine if the device just picked up its own
+broadcast (which is valid).</li>
+<li>The service type is checked, to verify it's a type of service your
+application can connect to.</li>
+<li>The service name is checked to verify connection to the correct
+application.</li>
+</ol>
+
+<p>Checking the service name isn't always necessary, and is only relevant if you
+want to connect to a specific application.  For instance, the application might
+only want to connect to instances of itself running on other devices.  However, if the
+application wants to connect to a network printer, it's enough to see that the service type
+is "_ipp._tcp".</p>
+
+<p>After setting up the listener, call {@link android.net.nsd.NsdManager#discoverServices(String, int,
+NsdManager.DiscoveryListener) discoverServices()}, passing in the service type
+your application should look for, the discovery protocol to use, and the
+listener you just created.</p>
+
+<pre>
+    mNsdManager.discoverServices(
+        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
+</pre>
+
+
+<h2 id="connect">Connect to Services on the Network</h2>
+<p>When your application finds a service on the network to connect to, it
+must first determine the connection information for that service, using the
+{@link android.net.nsd.NsdManager#resolveService resolveService()} method.
+Implement a {@link android.net.nsd.NsdManager.ResolveListener} to pass into this
+method, and use it to get a {@link android.net.nsd.NsdServiceInfo} containing
+the connection information.</p>
+
+<pre>
+public void initializeResolveListener() {
+    mResolveListener = new NsdManager.ResolveListener() {
+
+        &#64;Override
+        public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            // Called when the resolve fails.  Use the error code to debug.
+            Log.e(TAG, "Resolve failed" + errorCode);
+        }
+
+        &#64;Override
+        public void onServiceResolved(NsdServiceInfo serviceInfo) {
+            Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
+
+            if (serviceInfo.getServiceName().equals(mServiceName)) {
+                Log.d(TAG, "Same IP.");
+                return;
+            }
+            mService = serviceInfo;
+            int port = mService.getPort();
+            InetAddress host = mService.getHost();
+        }
+    };
+}
+</pre>
+
+<p>Once the service is resolved, your application receives detailed
+service information including an IP address and port number.  This is  everything
+you need to create your own network connection to the service.</p>
+
+
+<h2 id="teardown">Unregister Your Service on Application Close</h2>
+<p>It's important to enable and disable NSD
+functionality as appropriate during the application's
+lifecycle.  Unregistering your application when it closes down helps prevent
+other applications from thinking it's still active and attempting to connect to
+it.  Also, service discovery is an expensive operation, and should be stopped
+when the parent Activity is paused, and re-enabled when the Activity is
+resumed.  Override the lifecycle methods of your main Activity and insert code
+to start and stop service broadcast and discovery as appropriate.</p>
+
+<pre>
+//In your application's Activity
+
+    &#64;Override
+    protected void onPause() {
+        if (mNsdHelper != null) {
+            mNsdHelper.tearDown();
+        }
+        super.onPause();
+    }
+
+    &#64;Override
+    protected void onResume() {
+        super.onResume();
+        if (mNsdHelper != null) {
+            mNsdHelper.registerService(mConnection.getLocalPort());
+            mNsdHelper.discoverServices();
+        }
+    }
+
+    &#64;Override
+    protected void onDestroy() {
+        mNsdHelper.tearDown();
+        mConnection.tearDown();
+        super.onDestroy();
+    }
+
+    // NsdHelper's tearDown method
+        public void tearDown() {
+        mNsdManager.unregisterService(mRegistrationListener);
+        mNsdManager.stopServiceDiscovery(mDiscoveryListener);
+    }
+</pre>
+
diff --git a/docs/html/training/connect-devices-wirelessly/wifi-direct.jd b/docs/html/training/connect-devices-wirelessly/wifi-direct.jd
new file mode 100644
index 0000000..99bb243
--- /dev/null
+++ b/docs/html/training/connect-devices-wirelessly/wifi-direct.jd
@@ -0,0 +1,372 @@
+page.title=Connecting with Wi-Fi Direct
+parent.title=Connecting Devices Wirelessly
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Using Network Service Discovery
+previous.link=nsd.html
+next.title=Service Discovery with Wi-Fi Direct
+next.link=nsd-wifi-direct.html
+
+@jd:body
+
+<div id="tb-wrapper">
+  <div id="tb">
+    <h2>This lesson teaches you how to</h2>
+    <ol>
+      <li><a href="#permissions">Set Up Application Permissions</a></li>
+      <li><a href="#receiver">Set Up the Broadcast Receiver and Peer-to-Peer
+        Manager</a></li>
+      <li><a href="#discover">Initiate Peer Discovery</a></li>
+      <li><a href="#fetch">Fetch the List of Peers</a></li>
+      <li><a href="#connect">Connect to a Peer</a></li>
+    </ol>
+  </div>
+</div>
+
+<p>The Wi-Fi Direct&trade; APIs allow applications to connect to nearby devices without
+needing to connect to a network or hotspot.  This allows your application to quickly
+find and interact with nearby devices, at a range beyond the capabilities of Bluetooth.
+</p>
+<p>
+This lesson shows you how to find and connect to nearby devices using Wi-Fi Direct.
+</p>
+<h2 id="permissions">Set Up Application Permissions</h2>
+<p>In order to use Wi-Fi Direct, add the {@link
+android.Manifest.permission#CHANGE_WIFI_STATE}, {@link
+android.Manifest.permission#ACCESS_WIFI_STATE},
+and {@link android.Manifest.permission#INTERNET}
+permissions to your manifest.   Wi-Fi Direct doesn't require an internet connection,
+but it does use standard Java sockets, which require the {@link
+android.Manifest.permission#INTERNET} permission.
+So you need the following permissions to use Wi-Fi Direct.</p>
+
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.nsdchat"
+    ...
+
+    &lt;uses-permission
+        android:required="true"
+        android:name="android.permission.ACCESS_WIFI_STATE"/&gt;
+    &lt;uses-permission
+        android:required="true"
+        android:name="android.permission.CHANGE_WIFI_STATE"/&gt;
+    &lt;uses-permission
+        android:required="true"
+        android:name="android.permission.INTERNET"/&gt;
+    ...
+</pre>
+
+<h2 id="receiver">Set Up a Broadcast Receiver and Peer-to-Peer Manager</h2>
+<p>To use Wi-Fi Direct, you need to listen for broadcast intents that tell your
+application when certain events have occurred.  In your application, instantiate
+an {@link
+android.content.IntentFilter} and set it to listen for the following:</p>
+<dl>
+  <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_STATE_CHANGED_ACTION}</dt>
+  <dd>Indicates whether Wi-Fi Peer-To-Peer (P2P) is enabled</dd>
+  <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION}</dt>
+  <dd>Indicates that the available peer list has changed.</dd>
+  <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION}</dt>
+  <dd>Indicates the state of Wi-Fi P2P connectivity has changed.</dd>
+  <dt>{@link android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_THIS_DEVICE_CHANGED_ACTION}</dt>
+  <dd>Indicates this device's configuration details have changed.</dd>
+<pre>
+private final IntentFilter intentFilter = new IntentFilter();
+...
+&#64;Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.main);
+
+    //  Indicates a change in the Wi-Fi Peer-to-Peer status.
+    intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+
+    // Indicates a change in the list of available peers.
+    intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+
+    // Indicates the state of Wi-Fi P2P connectivity has changed.
+    intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+
+    // Indicates this device's details have changed.
+    intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
+
+    ...
+}
+</pre>
+
+  <p>At the end of the {@link android.app.Activity#onCreate onCreate()} method, get an instance of the {@link
+android.net.wifi.p2p.WifiP2pManager}, and call its {@link
+android.net.wifi.p2p.WifiP2pManager#initialize(Context, Looper, WifiP2pManager.ChannelListener) initialize()}
+method.  This method returns a {@link
+android.net.wifi.p2p.WifiP2pManager.Channel} object, which you'll use later to
+connect your app to the Wi-Fi Direct Framework.</p>
+
+<pre>
+&#64;Override
+
+Channel mChannel;
+
+public void onCreate(Bundle savedInstanceState) {
+    ....
+    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
+    mChannel = mManager.initialize(this, getMainLooper(), null);
+}
+</pre>
+<p>Now create a new {@link
+android.content.BroadcastReceiver} class that you'll use to listen for changes
+to the System's Wi-Fi P2P state.  In the {@link
+android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()}
+method, add a condition to handle each P2P state change listed above.</p>
+
+<pre>
+
+    &#64;Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
+            // Determine if Wifi Direct mode is enabled or not, alert
+            // the Activity.
+            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
+            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
+                activity.setIsWifiP2pEnabled(true);
+            } else {
+                activity.setIsWifiP2pEnabled(false);
+            }
+        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
+
+            // The peer list has changed!  We should probably do something about
+            // that.
+
+        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
+
+            // Connection state changed!  We should probably do something about
+            // that.
+
+        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
+            DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
+                    .findFragmentById(R.id.frag_list);
+            fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
+                    WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
+
+        }
+    }
+</pre>
+
+<p>Finally, add code to register the intent filter and broadcast receiver when
+your main activity is active, and unregister them when the activity is paused.
+The best place to do this is the {@link android.app.Activity#onResume()} and
+{@link android.app.Activity#onPause()} methods.
+
+<pre>
+    /** register the BroadcastReceiver with the intent values to be matched */
+    &#64;Override
+    public void onResume() {
+        super.onResume();
+        receiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
+        registerReceiver(receiver, intentFilter);
+    }
+
+    &#64;Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(receiver);
+    }
+</pre>
+
+
+<h2 id="discover">Initiate Peer Discovery</h2>
+<p>To start searching for nearby devices with Wi-Fi Direct, call {@link
+android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel,
+WifiP2pManager.ActionListener) discoverPeers()}.  This method takes the
+following arguments:</p>
+<ul>
+  <li>The {@link android.net.wifi.p2p.WifiP2pManager.Channel} you
+  received back when you initialized the peer-to-peer mManager</li>
+  <li>An implementation of {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} with methods
+  the system invokes for successful and unsuccessful discovery.</li>
+</ul>
+
+<pre>
+mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
+
+        &#64;Override
+        public void onSuccess() {
+            // Code for when the discovery initiation is successful goes here.
+            // No services have actually been discovered yet, so this method
+            // can often be left blank.  Code for peer discovery goes in the
+            // onReceive method, detailed below.
+        }
+
+        &#64;Override
+        public void onFailure(int reasonCode) {
+            // Code for when the discovery initiation fails goes here.
+            // Alert the user that something went wrong.
+        }
+});
+</pre>
+
+<p>Keep in mind that this only <em>initiates</em> peer discovery.  The
+{@link android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel,
+WifiP2pManager.ActionListener) discoverPeers()} method starts the discovery process and then
+immediately returns.  The system notifies you if the peer discovery process is
+successfully initiated by calling methods in the provided action listener.
+Also, discovery will remain active until a connection is initiated or a P2P group is
+formed.</p>
+
+<h2 id="fetch">Fetch the List of Peers</h2>
+<p>Now write the code that fetches and processes the list of peers.  First
+implement the {@link android.net.wifi.p2p.WifiP2pManager.PeerListListener}
+interface, which provides information about the peers that Wi-Fi Direct has
+detected.  The following code snippet illustrates this.</p>
+
+<pre>
+    private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();
+    ...
+
+    private PeerListListener peerListListener = new PeerListListener() {
+        &#64;Override
+        public void onPeersAvailable(WifiP2pDeviceList peerList) {
+
+            // Out with the old, in with the new.
+            peers.clear();
+            peers.addAll(peerList.getDeviceList());
+
+            // If an AdapterView is backed by this data, notify it
+            // of the change.  For instance, if you have a ListView of available
+            // peers, trigger an update.
+            ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();
+            if (peers.size() == 0) {
+                Log.d(WiFiDirectActivity.TAG, "No devices found");
+                return;
+            }
+        }
+    }
+</pre>
+
+<p>Now modify your broadcast receiver's {@link
+android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()}
+method to call {@link android.net.wifi.p2p.WifiP2pManager#requestPeers
+requestPeers()} when an intent with the action {@link
+android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} is received.  You
+need to pass this listener into the receiver somehow.  One way is to send it
+as an argument to the broadcast receiver's constructor.
+</p>
+
+<pre>
+public void onReceive(Context context, Intent intent) {
+    ...
+    else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
+
+        // Request available peers from the wifi p2p manager. This is an
+        // asynchronous call and the calling activity is notified with a
+        // callback on PeerListListener.onPeersAvailable()
+        if (mManager != null) {
+            mManager.requestPeers(mChannel, peerListener);
+        }
+        Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
+    }...
+}
+</pre>
+
+<p>Now, an intent with the action {@link
+android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} intent will
+trigger a request for an updated peer list. </p>
+
+<h2 id="connect">Connect to a Peer</h2>
+<p>In order to connect to a peer, create a new {@link
+android.net.wifi.p2p.WifiP2pConfig} object, and copy data into it from the
+{@link android.net.wifi.p2p.WifiP2pDevice} representing the device you want to
+connect to.  Then call the {@link
+android.net.wifi.p2p.WifiP2pManager#connect(WifiP2pManager.Channel,
+WifiP2pConfig, WifiP2pManager.ActionListener) connect()}
+method.</p>
+
+<pre>
+    &#64;Override
+    public void connect() {
+        // Picking the first device found on the network.
+        WifiP2pDevice device = peers.get(0);
+
+        WifiP2pConfig config = new WifiP2pConfig();
+        config.deviceAddress = device.deviceAddress;
+        config.wps.setup = WpsInfo.PBC;
+
+        mManager.connect(mChannel, config, new ActionListener() {
+
+            &#64;Override
+            public void onSuccess() {
+                // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
+            }
+
+            &#64;Override
+            public void onFailure(int reason) {
+                Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
+                        Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+</pre>
+
+<p>The {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} implemented in
+this snippet only notifies you when the <em>initiation</em> succeeds or fails.
+To listen for <em>changes</em> in connection state, implement the {@link
+android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener} interface. Its {@link
+android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener#onConnectionInfoAvailable(WifiP2pInfo)
+onConnectionInfoAvailable()}
+callback will notify you when the state of the connection changes.  In cases
+where multiple devices are going to be connected to a single device (like a game with
+3 or more players, or a chat app), one device will be designated the "group
+owner".</p>
+
+<pre>
+    &#64;Override
+    public void onConnectionInfoAvailable(final WifiP2pInfo info) {
+
+        // InetAddress from WifiP2pInfo struct.
+        InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());
+
+        // After the group negotiation, we can determine the group owner.
+        if (info.groupFormed && info.isGroupOwner) {
+            // Do whatever tasks are specific to the group owner.
+            // One common case is creating a server thread and accepting
+            // incoming connections.
+        } else if (info.groupFormed) {
+            // The other device acts as the client. In this case,
+            // you'll want to create a client thread that connects to the group
+            // owner.
+        }
+    }
+</pre>
+
+<p>Now go back to the {@link
+android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} method of the broadcast receiver, and modify the section
+that listens for a {@link
+android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} intent.
+When this intent is received, call {@link
+android.net.wifi.p2p.WifiP2pManager#requestConnectionInfo(WifiP2pManager.Channel,
+WifiP2pManager.ConnectionInfoListener) requestConnectionInfo()}.  This is an
+asynchronous call, so results will be received by the connection info listener
+you provide as a parameter.
+
+<pre>
+        ...
+        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
+
+            if (mManager == null) {
+                return;
+            }
+
+            NetworkInfo networkInfo = (NetworkInfo) intent
+                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
+
+            if (networkInfo.isConnected()) {
+
+                // We are connected with the other device, request connection
+                // info to find group owner IP
+
+                mManager.requestConnectionInfo(mChannel, connectionListener);
+            }
+            ...
+</pre>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 37b69d4..b70ba3f 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -673,6 +673,27 @@
       </li>
 
 
+      <li class="nav-section">
+        <div class="nav-section-header"><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/index.html">
+            <span class="en">Connecting Devices Wirelessly</span>
+          </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/nsd.html">
+            <span class="en">Using Network Service Discovery</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/wifi-direct.html">
+            <span class="en">Connecting with Wi-Fi Direct</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/connect-devices-wirelessly/nsd-wifi-direct.html">
+            <span class="en">Using Wi-Fi Direct for Service Discovery</span>
+          </a>
+          </li>
+        </ul>
+      </li>
+
+
     </ul>
   </li>
 </ul><!-- nav -->