WiFi Direct Service Discovery sample

Change-Id: Id5ac219cbf03a8a96203ed682f729df9bbb59fcd
diff --git a/samples/WiFiDirectServiceDiscovery/Android.mk b/samples/WiFiDirectServiceDiscovery/Android.mk
new file mode 100644
index 0000000..1682aa2
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WiFiDirectServiceDiscovery
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/WiFiDirectServiceDiscovery/AndroidManifest.xml b/samples/WiFiDirectServiceDiscovery/AndroidManifest.xml
new file mode 100644
index 0000000..17e4fd5
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.wifidirect.discovery"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="16" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <!-- Google Play filtering --> 
+    <uses-feature android:name="android.hardware.wifi.direct" android:required="true"/>
+    
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+        <activity
+            android:name=".WiFiServiceDiscoveryActivity"
+            android:label="@string/app_name" 
+            android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/_index.html b/samples/WiFiDirectServiceDiscovery/_index.html
new file mode 100644
index 0000000..58f5962
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/_index.html
@@ -0,0 +1,52 @@
+<p>This is a demo application highlighting how to advertise and discover local services that are Wi-Fi peer to peer network capable with
+the Wi-Fi Direct Service Discovery APIs.  Service discovery on Wi-Fi direct allows applications to discover and enagage with  peers that support a certain service. 
+As an example, a gaming application can find and associate with devices that support the game. This application allows you to chat with a peer after a succesful connection.</p>
+
+<p>The source code for this demo app shows how to accomplish three key things
+with Wi-Fi Direct  Service Discovery APIs: Advertise services, discover services and connect to peers advertising such services</p>
+
+<p>The application includes:<p>
+<ul> <li><a
+    href="src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.html"><code>WiFiServiceDiscoveryActivity</code></a>
+  &mdash; the main <code>Activity</code> that contains two fragments to handle app's UI. It advertises and discovers services and also registers a broadcast receiver for Wi-Fi Direct related events.</li> <li><a
+    href="src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.html"><code>
+      WiFiDirectBroadcastReceiver</code></a> &mdash; a <code>BroadcastReceiver</code>
+  that listens for Wi-Fi Direct related events and passes them to
+  <code>WiFiServiceDiscoveryActivity</code> and it's fragments for neccesary action.</li> <li><a
+    href="src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.html"><code>WiFiDirectServicesList</code></a>
+  &mdash; a <code>ListFragment</code> that displays available services, peers and their status. </li>
+<li><a href="src/com/example/android/wifidirect/discovery/WiFiChatFragment.html"><code>WiFiChatFragment</code></a>
+  &mdash; a <code>Fragment</code> that displays handles chat UI </li>
+<li><a href="src/com/example/android/wifidirect/discovery/ChatManager.html"><code>ChatManager</code></a>
+  &mdash; a <code>Runnable</code> that continously performs Socket I/O.</li>
+<li><a href="src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.html"><code>GroupOwnerSocketHandler</code></a>
+  &mdash; a <code>Thread</code> that implements a server side Socket handler and spawns a client socket per connection.</li> </ul>
+<li><a href="src/com/example/android/wifidirect/discovery/ClientSocketHandler.html"><code>GroupOwnerSocketHandler</code></a>
+  &mdash; a <code>Thread</code> that implements a client side Socket handler.</li> </ul>
+<p>If you are developing an application that uses the Wi-Fi Direct Service Discovery APIs, remember that the
+feature is supported only on Android 4.1 (API level 16) and higher versions of
+the platform. To ensure that your application can only
+be installed on devices that are capable of supporting Wi-Fi Direct Service Discovery, remember to add the
+following to the application's manifest before publishing to Google Play:</p>
+<ul> <li><code>&lt;uses-sdk android:minSdkVersion="16" /&gt;</code>, which
+  indicates to Google Play and the platform that your application requires
+  Android 4.1 or higher. For more information, see <a
+    href="../../../guide/appendix/api-levels.html">API Levels</a> and the
+  documentation for the <a
+    href="../../../guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a>
+  element.</li> </ul> <p>To control how Google Play filters your application
+from devices that do not support Wi-Fi Direct mode, remember to add the following to the
+application's manifest <ul> <li><code>&lt;uses-feature
+    android:name="android.hardware.wifi.direct" /&gt;</code>, which tells Google
+  Play that your application uses the Wi-Fi Direct API. The declaration should include
+  an <code>android:required</code> attribute that indicates whether you want
+  Google Play to filter the application from devices that do not offer Wi-Fi Direct support. Other <code>&lt;uses-feature&gt;</code> declarations may also be
+  needed, depending on your implementation. For more information, see the
+  documentation for the <a
+    href="../../../guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a>
+  element.</li> </ul>
+<p>For more information about using the Wi-Fi Direct Service Discovery APIs, see the <a
+  href="../../../reference/android/net/wifi/p2p/package-summary.html"><code>android.net.wifi.p2p	</a></code>
+documentation. </p>
+
+<img alt="" src="../images/WifiDirect.png" />
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml b/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml
new file mode 100644
index 0000000..d7f0af9
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:layout_gravity="top"
+    android:gravity="top" >
+
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+    </ListView>
+
+    <LinearLayout
+        android:id="@android:id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="10dip">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="20sp"
+            android:text="@string/finding_service" >
+        </TextView>
+
+         <ProgressBar
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="right"
+            android:layout_marginLeft="20dip"
+            style="@android:style/Widget.ProgressBar.Small" >
+        </ProgressBar>
+       
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/fragment_chat.xml b/samples/WiFiDirectServiceDiscovery/res/layout/fragment_chat.xml
new file mode 100644
index 0000000..7a6fba4
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/fragment_chat.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginBottom="50dip"
+        android:transcriptMode="alwaysScroll" >
+
+        <!-- Preview: listitem=@android:layout/simple_list_item_1 -->
+
+    </ListView>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="50dip"
+        android:layout_gravity="bottom" >
+
+        <EditText
+            android:id="@+id/txtChatLine"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_weight="0.90"
+            android:focusableInTouchMode="true"
+            android:hint="@string/txt_hint"
+            android:visibility="visible" >
+        </EditText>
+
+        <Button
+            android:id="@+id/button1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|right"
+            android:layout_weight="0.10"
+            android:text="@string/btn_send" />
+    </LinearLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/main.xml b/samples/WiFiDirectServiceDiscovery/res/layout/main.xml
new file mode 100644
index 0000000..c1c1bf8
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/main.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/container_root"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layout_gravity="top"
+        android:orientation="vertical" />
+
+    <TextView
+        android:id="@+id/status_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom" >
+    </TextView>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/values/strings.xml b/samples/WiFiDirectServiceDiscovery/res/values/strings.xml
new file mode 100644
index 0000000..fa6090f
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="finding_service">Finding local services</string>
+    <string name="btn_send">Send</string>
+    <string name="txt_hint">Type Message</string>
+    <string name="app_name">Wi-Fi Discovery</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/values/style.xml b/samples/WiFiDirectServiceDiscovery/res/values/style.xml
new file mode 100644
index 0000000..3742271
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/values/style.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="boldText">
+        <item name="android:textStyle">bold|italic</item>
+        <item name="android:textColor">#99CC00</item>
+    </style>
+
+    <style name="normalText">
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#33B5E5</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ChatManager.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ChatManager.java
new file mode 100644
index 0000000..d537f10
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ChatManager.java
@@ -0,0 +1,76 @@
+
+package com.example.android.wifidirect.discovery;
+
+import android.os.Handler;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * Handles reading and writing of messages with socket buffers. Uses a Handler
+ * to post messages to UI thread for UI updates.
+ */
+public class ChatManager implements Runnable {
+
+    private Socket socket = null;
+    private Handler handler;
+
+    public ChatManager(Socket socket, Handler handler) {
+        this.socket = socket;
+        this.handler = handler;
+    }
+
+    private InputStream iStream;
+    private OutputStream oStream;
+    private static final String TAG = "ChatHandler";
+
+    @Override
+    public void run() {
+        try {
+
+            iStream = socket.getInputStream();
+            oStream = socket.getOutputStream();
+            byte[] buffer = new byte[1024];
+            int bytes;
+            handler.obtainMessage(WiFiServiceDiscoveryActivity.MY_HANDLE, this)
+                    .sendToTarget();
+
+            while (true) {
+                try {
+                    // Read from the InputStream
+                    bytes = iStream.read(buffer);
+                    if (bytes == -1) {
+                        break;
+                    }
+
+                    // Send the obtained bytes to the UI Activity
+                    Log.d(TAG, "Rec:" + String.valueOf(buffer));
+                    handler.obtainMessage(WiFiServiceDiscoveryActivity.MESSAGE_READ,
+                            bytes, -1, buffer).sendToTarget();
+                } catch (IOException e) {
+                    Log.e(TAG, "disconnected", e);
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                socket.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void write(byte[] buffer) {
+        try {
+            oStream.write(buffer);
+        } catch (IOException e) {
+            Log.e(TAG, "Exception during write", e);
+        }
+    }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ClientSocketHandler.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ClientSocketHandler.java
new file mode 100644
index 0000000..a182fd8
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ClientSocketHandler.java
@@ -0,0 +1,49 @@
+
+package com.example.android.wifidirect.discovery;
+
+import android.os.Handler;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+public class ClientSocketHandler extends Thread {
+
+    private static final String TAG = "ClientSocketHandler";
+    private Handler handler;
+    private ChatManager chat;
+    private InetAddress mAddress;
+
+    public ClientSocketHandler(Handler handler, InetAddress groupOwnerAddress) {
+        this.handler = handler;
+        this.mAddress = groupOwnerAddress;
+    }
+
+    @Override
+    public void run() {
+        Socket socket = new Socket();
+        try {
+            socket.bind(null);
+            socket.connect(new InetSocketAddress(mAddress.getHostAddress(),
+                    WiFiServiceDiscoveryActivity.SERVER_PORT), 5000);
+            Log.d(TAG, "Launching the I/O handler");
+            chat = new ChatManager(socket, handler);
+            new Thread(chat).start();
+        } catch (IOException e) {
+            e.printStackTrace();
+            try {
+                socket.close();
+            } catch (IOException e1) {
+                e1.printStackTrace();
+            }
+            return;
+        }
+    }
+
+    public ChatManager getChat() {
+        return chat;
+    }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.java
new file mode 100644
index 0000000..483d817
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.java
@@ -0,0 +1,67 @@
+
+package com.example.android.wifidirect.discovery;
+
+import android.os.Handler;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The implementation of a ServerSocket handler. This is used by the wifi p2p
+ * group owner.
+ */
+public class GroupOwnerSocketHandler extends Thread {
+
+    ServerSocket socket = null;
+    private final int THREAD_COUNT = 10;
+    private Handler handler;
+    private static final String TAG = "GroupOwnerSocketHandler";
+
+    public GroupOwnerSocketHandler(Handler handler) throws IOException {
+        try {
+            socket = new ServerSocket(4545);
+            this.handler = handler;
+            Log.d("GroupOwnerSocketHandler", "Socket Started");
+        } catch (IOException e) {
+            e.printStackTrace();
+            pool.shutdownNow();
+            throw e;
+        }
+
+    }
+
+    /**
+     * A ThreadPool for client sockets.
+     */
+    private final ThreadPoolExecutor pool = new ThreadPoolExecutor(
+            THREAD_COUNT, THREAD_COUNT, 10, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<Runnable>());
+
+    @Override
+    public void run() {
+        while (true) {
+            try {
+                // A blocking operation. Initiate a ChatManager instance when
+                // there is a new connection
+                pool.execute(new ChatManager(socket.accept(), handler));
+                Log.d(TAG, "Launching the I/O handler");
+
+            } catch (IOException e) {
+                try {
+                    if (socket != null && !socket.isClosed())
+                        socket.close();
+                } catch (IOException ioe) {
+
+                }
+                e.printStackTrace();
+                pool.shutdownNow();
+                break;
+            }
+        }
+    }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiChatFragment.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiChatFragment.java
new file mode 100644
index 0000000..e4a1858
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiChatFragment.java
@@ -0,0 +1,109 @@
+
+package com.example.android.wifidirect.discovery;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This fragment handles chat related UI which includes a list view for messages
+ * and a message entry field with send button.
+ */
+public class WiFiChatFragment extends Fragment {
+
+    private View view;
+    private ChatManager chatManager;
+    private TextView chatLine;
+    private ListView listView;
+    ChatMessageAdapter adapter = null;
+    private List<String> items = new ArrayList<String>();
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        view = inflater.inflate(R.layout.fragment_chat, container, false);
+        chatLine = (TextView) view.findViewById(R.id.txtChatLine);
+        listView = (ListView) view.findViewById(android.R.id.list);
+        adapter = new ChatMessageAdapter(getActivity(), android.R.id.text1,
+                items);
+        listView.setAdapter(adapter);
+        view.findViewById(R.id.button1).setOnClickListener(
+                new View.OnClickListener() {
+
+                    @Override
+                    public void onClick(View arg0) {
+                        if (chatManager != null) {
+                            chatManager.write(chatLine.getText().toString()
+                                    .getBytes());
+                            pushMessage("Me: " + chatLine.getText().toString());
+                            chatLine.setText("");
+                            chatLine.clearFocus();
+                        }
+                    }
+                });
+        return view;
+    }
+
+    public interface MessageTarget {
+        public Handler getHandler();
+    }
+
+    public void setChatManager(ChatManager obj) {
+        chatManager = obj;
+    }
+
+    public void pushMessage(String readMessage) {
+        adapter.add(readMessage);
+        adapter.notifyDataSetChanged();
+    }
+
+    /**
+     * ArrayAdapter to manage chat messages.
+     */
+    public class ChatMessageAdapter extends ArrayAdapter<String> {
+
+        List<String> messages = null;
+
+        public ChatMessageAdapter(Context context, int textViewResourceId,
+                List<String> items) {
+            super(context, textViewResourceId, items);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v = convertView;
+            if (v == null) {
+                LayoutInflater vi = (LayoutInflater) getActivity()
+                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                v = vi.inflate(android.R.layout.simple_list_item_1, null);
+            }
+            String message = items.get(position);
+            if (message != null && !message.isEmpty()) {
+                TextView nameText = (TextView) v
+                        .findViewById(android.R.id.text1);
+
+                if (nameText != null) {
+                    nameText.setText(message);
+                    if (message.startsWith("Me: ")) {
+                        nameText.setTextAppearance(getActivity(),
+                                R.style.normalText);
+                    } else {
+                        nameText.setTextAppearance(getActivity(),
+                                R.style.boldText);
+                    }
+                }
+            }
+            return v;
+        }
+    }
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.java
new file mode 100644
index 0000000..d606dba
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.java
@@ -0,0 +1,91 @@
+
+package com.example.android.wifidirect.discovery;
+
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.WifiP2pManager.Channel;
+import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
+import android.util.Log;
+
+/**
+ * A BroadcastReceiver that notifies of important wifi p2p events.
+ */
+public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
+
+    private WifiP2pManager manager;
+    private Channel channel;
+    private Activity activity;
+
+    /**
+     * @param manager WifiP2pManager system service
+     * @param channel Wifi p2p channel
+     * @param activity activity associated with the receiver
+     */
+    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
+            Activity activity) {
+        super();
+        this.manager = manager;
+        this.channel = channel;
+        this.activity = activity;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.content.BroadcastReceiver#onReceive(android.content.Context,
+     * android.content.Intent)
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        Log.d(WiFiServiceDiscoveryActivity.TAG, action);
+        if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
+
+            if (manager == 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
+                Log.d(WiFiServiceDiscoveryActivity.TAG,
+                        "Connected to p2p network. Requesting network details");
+                manager.requestConnectionInfo(channel,
+                        (ConnectionInfoListener) activity);
+            } else {
+                // It's a disconnect
+            }
+        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
+                .equals(action)) {
+
+            WifiP2pDevice device = (WifiP2pDevice) intent
+                    .getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
+            Log.d(WiFiServiceDiscoveryActivity.TAG, "Device status -" + device.status);
+
+        }
+    }
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.java
new file mode 100644
index 0000000..eea59a1
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.java
@@ -0,0 +1,107 @@
+
+package com.example.android.wifidirect.discovery;
+
+import android.app.ListFragment;
+import android.content.Context;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A simple ListFragment that shows the available services as published by the
+ * peers
+ */
+public class WiFiDirectServicesList extends ListFragment {
+
+    WiFiDevicesAdapter listAdapter = null;
+
+    interface DeviceClickListener {
+        public void connectP2p(WiFiP2pService wifiP2pService);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.devices_list, container, false);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        listAdapter = new WiFiDevicesAdapter(this.getActivity(),
+                android.R.layout.simple_list_item_2, android.R.id.text1,
+                new ArrayList<WiFiP2pService>());
+        setListAdapter(listAdapter);
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        // TODO Auto-generated method stub
+        ((DeviceClickListener) getActivity()).connectP2p((WiFiP2pService) l
+                .getItemAtPosition(position));
+        ((TextView) v.findViewById(android.R.id.text2)).setText("Connecting");
+
+    }
+
+    public class WiFiDevicesAdapter extends ArrayAdapter<WiFiP2pService> {
+
+        private List<WiFiP2pService> items;
+
+        public WiFiDevicesAdapter(Context context, int resource,
+                int textViewResourceId, List<WiFiP2pService> items) {
+            super(context, resource, textViewResourceId, items);
+            this.items = items;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v = convertView;
+            if (v == null) {
+                LayoutInflater vi = (LayoutInflater) getActivity()
+                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                v = vi.inflate(android.R.layout.simple_list_item_2, null);
+            }
+            WiFiP2pService service = items.get(position);
+            if (service != null) {
+                TextView nameText = (TextView) v
+                        .findViewById(android.R.id.text1);
+
+                if (nameText != null) {
+                    nameText.setText(service.device.deviceName + " - " + service.instanceName);
+                }
+                TextView statusText = (TextView) v
+                        .findViewById(android.R.id.text2);
+                statusText.setText(getDeviceStatus(service.device.status));
+            }
+            return v;
+        }
+
+    }
+
+    public static String getDeviceStatus(int statusCode) {
+        switch (statusCode) {
+            case WifiP2pDevice.CONNECTED:
+                return "Connected";
+            case WifiP2pDevice.INVITED:
+                return "Invited";
+            case WifiP2pDevice.FAILED:
+                return "Failed";
+            case WifiP2pDevice.AVAILABLE:
+                return "Available";
+            case WifiP2pDevice.UNAVAILABLE:
+                return "Unavailable";
+            default:
+                return "Unknown";
+
+        }
+    }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiP2pService.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiP2pService.java
new file mode 100644
index 0000000..882f78e
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiP2pService.java
@@ -0,0 +1,13 @@
+
+package com.example.android.wifidirect.discovery;
+
+import android.net.wifi.p2p.WifiP2pDevice;
+
+/**
+ * A structure to hold service information.
+ */
+public class WiFiP2pService {
+    WifiP2pDevice device;
+    String instanceName = null;
+    String serviceRegistrationType = null;
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.java
new file mode 100644
index 0000000..20e85cb
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.java
@@ -0,0 +1,346 @@
+
+package com.example.android.wifidirect.discovery;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.WifiP2pManager.ActionListener;
+import android.net.wifi.p2p.WifiP2pManager.Channel;
+import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
+import android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener;
+import android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener;
+import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.example.android.wifidirect.discovery.WiFiChatFragment.MessageTarget;
+import com.example.android.wifidirect.discovery.WiFiDirectServicesList.DeviceClickListener;
+import com.example.android.wifidirect.discovery.WiFiDirectServicesList.WiFiDevicesAdapter;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The main activity for the sample. This activity registers a local service and
+ * perform discovery over Wi-Fi p2p network. It also hosts a couple of fragments
+ * to manage chat operations. When the app is launched, the device publishes a
+ * chat service and also tries to discover services published by other peers. On
+ * selecting a peer published service, the app initiates a Wi-Fi P2P (Direct)
+ * connection with the peer. On successful connection with a peer advertising
+ * the same service, the app opens up sockets to initiate a chat.
+ * {@code WiFiChatFragment} is then added to the the main activity which manages
+ * the interface and messaging needs for a chat session.
+ */
+public class WiFiServiceDiscoveryActivity extends Activity implements
+        DeviceClickListener, Handler.Callback, MessageTarget,
+        ConnectionInfoListener {
+
+    public static final String TAG = "wifidirectdemo";
+
+    // TXT RECORD properties
+    public static final String TXTRECORD_PROP_AVAILABLE = "available";
+    public static final String SERVICE_INSTANCE = "_wifidemotest";
+    public static final String SERVICE_REG_TYPE = "_presence._tcp";
+
+    public static final int MESSAGE_READ = 0x400 + 1;
+    public static final int MY_HANDLE = 0x400 + 2;
+    private WifiP2pManager manager;
+
+    static final int SERVER_PORT = 4545;
+
+    private final IntentFilter intentFilter = new IntentFilter();
+    private Channel channel;
+    private BroadcastReceiver receiver = null;
+    private WifiP2pDnsSdServiceRequest serviceRequest;
+
+    private Handler handler = new Handler(this);
+    private WiFiChatFragment chatFragment;
+    private WiFiDirectServicesList servicesList;
+
+    private TextView statusTxtView;
+
+    public Handler getHandler() {
+        return handler;
+    }
+
+    public void setHandler(Handler handler) {
+        this.handler = handler;
+    }
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+        statusTxtView = (TextView) findViewById(R.id.status_text);
+
+        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+        intentFilter
+                .addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+        intentFilter
+                .addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
+
+        manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
+        channel = manager.initialize(this, getMainLooper(), null);
+        startRegistrationAndDiscovery();
+
+        servicesList = new WiFiDirectServicesList();
+        getFragmentManager().beginTransaction()
+                .add(R.id.container_root, servicesList, "services").commit();
+
+    }
+
+    @Override
+    protected void onRestart() {
+        Fragment frag = getFragmentManager().findFragmentByTag("services");
+        if (frag != null) {
+            getFragmentManager().beginTransaction().remove(frag).commit();
+        }
+        super.onRestart();
+    }
+
+    @Override
+    protected void onStop() {
+        if (manager != null && channel != null) {
+            manager.removeGroup(channel, new ActionListener() {
+
+                @Override
+                public void onFailure(int reasonCode) {
+                    Log.d(TAG, "Disconnect failed. Reason :" + reasonCode);
+                }
+
+                @Override
+                public void onSuccess() {
+                }
+
+            });
+        }
+        super.onStop();
+    }
+
+    /**
+     * Registers a local service and then initiates a service discovery
+     */
+    private void startRegistrationAndDiscovery() {
+        Map<String, String> record = new HashMap<String, String>();
+        record.put(TXTRECORD_PROP_AVAILABLE, "visible");
+
+        WifiP2pDnsSdServiceInfo service = WifiP2pDnsSdServiceInfo.newInstance(
+                SERVICE_INSTANCE, SERVICE_REG_TYPE, record);
+        manager.addLocalService(channel, service, new ActionListener() {
+
+            @Override
+            public void onSuccess() {
+                appendStatus("Added Local Service");
+            }
+
+            @Override
+            public void onFailure(int error) {
+                appendStatus("Failed to add a service");
+            }
+        });
+
+        discoverService();
+
+    }
+
+    private void discoverService() {
+
+        /*
+         * Register listeners for DNS-SD services. These are callbacks invoked
+         * by the system when a service is actually discovered.
+         */
+
+        manager.setDnsSdResponseListeners(channel,
+                new DnsSdServiceResponseListener() {
+
+                    @Override
+                    public void onDnsSdServiceAvailable(String instanceName,
+                            String registrationType, WifiP2pDevice srcDevice) {
+
+                        // A service has been discovered. Is this our app?
+
+                        if (instanceName.equalsIgnoreCase(SERVICE_INSTANCE)) {
+
+                            // update the UI and add the item the discovered
+                            // device.
+                            WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
+                                    .findFragmentByTag("services");
+                            if (fragment != null) {
+                                WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
+                                        .getListAdapter());
+                                WiFiP2pService service = new WiFiP2pService();
+                                service.device = srcDevice;
+                                service.instanceName = instanceName;
+                                service.serviceRegistrationType = registrationType;
+                                adapter.add(service);
+                                adapter.notifyDataSetChanged();
+                                Log.d(TAG, "onBonjourServiceAvailable "
+                                        + instanceName);
+                            }
+                        }
+
+                    }
+                }, new DnsSdTxtRecordListener() {
+
+                    /**
+                     * A new TXT record is available. Pick up the advertised
+                     * buddy name.
+                     */
+                    @Override
+                    public void onDnsSdTxtRecordAvailable(
+                            String fullDomainName, Map<String, String> record,
+                            WifiP2pDevice device) {
+                        Log.d(TAG,
+                                device.deviceName + " is "
+                                        + record.get(TXTRECORD_PROP_AVAILABLE));
+                    }
+                });
+
+        // After attaching listeners, create a service request and initiate
+        // discovery.
+        serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
+        manager.addServiceRequest(channel, serviceRequest,
+                new ActionListener() {
+
+                    @Override
+                    public void onSuccess() {
+                        appendStatus("Added service discovery request");
+                    }
+
+                    @Override
+                    public void onFailure(int arg0) {
+                        appendStatus("Failed adding service discovery request");
+                    }
+                });
+        manager.discoverServices(channel, new ActionListener() {
+
+            @Override
+            public void onSuccess() {
+                appendStatus("Service discovery initiated");
+            }
+
+            @Override
+            public void onFailure(int arg0) {
+                appendStatus("Service discovery failed");
+
+            }
+        });
+    }
+
+    @Override
+    public void connectP2p(WiFiP2pService service) {
+        WifiP2pConfig config = new WifiP2pConfig();
+        config.deviceAddress = service.device.deviceAddress;
+        config.wps.setup = WpsInfo.PBC;
+        if (serviceRequest != null)
+            manager.removeServiceRequest(channel, serviceRequest,
+                    new ActionListener() {
+
+                        @Override
+                        public void onSuccess() {
+                        }
+
+                        @Override
+                        public void onFailure(int arg0) {
+                        }
+                    });
+
+        manager.connect(channel, config, new ActionListener() {
+
+            @Override
+            public void onSuccess() {
+                appendStatus("Connecting to service");
+            }
+
+            @Override
+            public void onFailure(int errorCode) {
+                appendStatus("Failed connecting to service");
+            }
+        });
+    }
+
+    @Override
+    public boolean handleMessage(Message msg) {
+        switch (msg.what) {
+            case MESSAGE_READ:
+                byte[] readBuf = (byte[]) msg.obj;
+                // construct a string from the valid bytes in the buffer
+                String readMessage = new String(readBuf, 0, msg.arg1);
+                Log.d(TAG, readMessage);
+                (chatFragment).pushMessage("Buddy: " + readMessage);
+                break;
+
+            case MY_HANDLE:
+                Object obj = msg.obj;
+                (chatFragment).setChatManager((ChatManager) obj);
+
+        }
+        return true;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
+        registerReceiver(receiver, intentFilter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(receiver);
+    }
+
+    @Override
+    public void onConnectionInfoAvailable(WifiP2pInfo p2pInfo) {
+        Thread handler = null;
+        /*
+         * The group owner accepts connections using a server socket and then spawns a
+         * client socket for every client. This is handled by {@code
+         * GroupOwnerSocketHandler}
+         */
+
+        if (p2pInfo.isGroupOwner) {
+            Log.d(TAG, "Connected as group owner");
+            try {
+                handler = new GroupOwnerSocketHandler(
+                        ((MessageTarget) this).getHandler());
+                handler.start();
+            } catch (IOException e) {
+                Log.d(TAG,
+                        "Failed to create a server thread - " + e.getMessage());
+                return;
+            }
+        } else {
+            Log.d(TAG, "Connected as peer");
+            handler = new ClientSocketHandler(
+                    ((MessageTarget) this).getHandler(),
+                    p2pInfo.groupOwnerAddress);
+            handler.start();
+        }
+        chatFragment = new WiFiChatFragment();
+        getFragmentManager().beginTransaction()
+                .replace(R.id.container_root, chatFragment).commit();
+        statusTxtView.setVisibility(View.GONE);
+    }
+
+    public void appendStatus(String status) {
+        String current = statusTxtView.getText().toString();
+        statusTxtView.setText(current + "\n" + status);
+    }
+}