diff --git a/samples/WiFiDirectServiceDiscovery/Android.mk b/samples/WiFiDirectServiceDiscovery/Android.mk
new file mode 100644
index 000000000..1682aa283
--- /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 000000000..17e4fd555
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/_index.html b/samples/WiFiDirectServiceDiscovery/_index.html
new file mode 100644
index 000000000..58f5962b9
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/_index.html
@@ -0,0 +1,52 @@
+
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.
+
+
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
+
+
The application includes:
+
WiFiServiceDiscoveryActivity
+ — the main Activity 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.
+ WiFiDirectBroadcastReceiver — a BroadcastReceiver
+ that listens for Wi-Fi Direct related events and passes them to
+ WiFiServiceDiscoveryActivity and it's fragments for neccesary action.
WiFiDirectServicesList
+ — a ListFragment that displays available services, peers and their status.
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:
+
<uses-sdk android:minSdkVersion="16" />, which
+ indicates to Google Play and the platform that your application requires
+ Android 4.1 or higher. For more information, see API Levels and the
+ documentation for the <uses-sdk>
+ element.
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
<uses-feature
+ android:name="android.hardware.wifi.direct" />, which tells Google
+ Play that your application uses the Wi-Fi Direct API. The declaration should include
+ an android:required attribute that indicates whether you want
+ Google Play to filter the application from devices that do not offer Wi-Fi Direct support. Other <uses-feature> declarations may also be
+ needed, depending on your implementation. For more information, see the
+ documentation for the <uses-feature>
+ element.
+
For more information about using the Wi-Fi Direct Service Discovery APIs, see the android.net.wifi.p2p
+documentation.
+
+
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 000000000..96a442e5b
Binary files /dev/null and b/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png 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 000000000..99238729d
Binary files /dev/null and b/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png 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 000000000..359047dfa
Binary files /dev/null and b/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png 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 000000000..71c6d760f
Binary files /dev/null and b/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml b/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml
new file mode 100644
index 000000000..d7f0af9f6
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 000000000..7a6fba4e0
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/fragment_chat.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 000000000..c1c1bf8f8
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/main.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
\ 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 000000000..fa6090f55
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+
+ Finding local services
+ Send
+ Type Message
+ Wi-Fi Discovery
+
+
\ 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 000000000..3742271d9
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/values/style.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ 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 000000000..d537f1067
--- /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 000000000..a182fd825
--- /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 000000000..483d817e8
--- /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());
+
+ @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 000000000..e4a1858b0
--- /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 items = new ArrayList();
+
+ @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 {
+
+ List messages = null;
+
+ public ChatMessageAdapter(Context context, int textViewResourceId,
+ List 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 000000000..d606dbaa0
--- /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 000000000..eea59a1fc
--- /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());
+ 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 {
+
+ private List items;
+
+ public WiFiDevicesAdapter(Context context, int resource,
+ int textViewResourceId, List 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 000000000..882f78eef
--- /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 000000000..20e85cb6e
--- /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 record = new HashMap();
+ 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 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);
+ }
+}