Initial commit of NsdChat application

Change-Id: I157ddfe38b79f01e72e8b52fa16d666c92f08254
diff --git a/samples/training/NsdChat/Android.mk b/samples/training/NsdChat/Android.mk
new file mode 100644
index 0000000..8cea77b
--- /dev/null
+++ b/samples/training/NsdChat/Android.mk
@@ -0,0 +1,18 @@
+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 := NsdChat
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/training/NsdChat/AndroidManifest.xml b/samples/training/NsdChat/AndroidManifest.xml
new file mode 100644
index 0000000..bb5e73c
--- /dev/null
+++ b/samples/training/NsdChat/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.nsdchat"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="16"
+        android:targetSdkVersion="16" />
+    <uses-permission android:required="true" android:name="android.permission.INTERNET"/>
+
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+        <activity
+            android:name="com.example.android.nsdchat.NsdChatActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/samples/training/NsdChat/proguard.cfg b/samples/training/NsdChat/proguard.cfg
new file mode 100644
index 0000000..52f242d
--- /dev/null
+++ b/samples/training/NsdChat/proguard.cfg
@@ -0,0 +1,26 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Keep onClickListeners.
+-keepclassmembers class * extends android.app.Activity { 
+       public void *(android.view.View); 
+}
+
diff --git a/samples/training/NsdChat/project.properties b/samples/training/NsdChat/project.properties
new file mode 100644
index 0000000..9b84a6b
--- /dev/null
+++ b/samples/training/NsdChat/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-16
diff --git a/samples/training/NsdChat/res/drawable-hdpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/training/NsdChat/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/NsdChat/res/drawable-ldpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/training/NsdChat/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/NsdChat/res/drawable-mdpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/training/NsdChat/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/NsdChat/res/drawable-xhdpi/ic_launcher.png b/samples/training/NsdChat/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/training/NsdChat/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/NsdChat/res/layout/main.xml b/samples/training/NsdChat/res/layout/main.xml
new file mode 100644
index 0000000..e516458
--- /dev/null
+++ b/samples/training/NsdChat/res/layout/main.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 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.
+-->
+<LinearLayout 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:layout_width="match_parent"
+        android:layout_height="96dp"
+        android:orientation="horizontal" >
+
+        <Button
+            android:id="@+id/advertise_btn"
+            android:layout_width="96dp"
+            android:layout_height="64dp"
+            android:onClick="clickAdvertise"
+            android:text="@string/register" />
+
+        <Button
+            android:id="@+id/discover_btn"
+            android:layout_width="96dp"
+            android:layout_height="64dp"
+            android:onClick="clickDiscover"
+            android:text="@string/discover" />
+
+        <Button
+            android:id="@+id/connect_btn"
+            android:layout_width="96dp"
+            android:layout_height="64dp"
+            android:onClick="clickConnect"
+            android:text="@string/connect" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/status"
+        android:layout_width="fill_parent"
+        android:layout_height="200dp"
+        android:focusable="true" />
+
+    <EditText
+        android:id="@+id/chatInput"
+        android:layout_width="fill_parent"
+        android:layout_height="80dp"
+        android:inputType="text"
+        android:singleLine="true" />
+
+    <Button
+        android:id="@+id/send_btn"
+        android:layout_width="96dp"
+        android:layout_height="64dp"
+        android:onClick="clickSend"
+        android:text="@string/send" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/training/NsdChat/res/values/strings.xml b/samples/training/NsdChat/res/values/strings.xml
new file mode 100644
index 0000000..1e15e36
--- /dev/null
+++ b/samples/training/NsdChat/res/values/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 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.
+-->
+<resources>
+
+    <string name="app_name">NsdChat</string>
+    
+    <string name="connect">Connect</string>
+    <string name="discover">Discover</string>
+    <string name="register">Register</string>
+    <string name="send">Send</string>
+
+</resources>
diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java b/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java
new file mode 100644
index 0000000..80aa9fd
--- /dev/null
+++ b/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package com.example.android.nsdchat;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+public class ChatConnection {
+
+    private Handler mUpdateHandler;
+    private ChatServer mChatServer;
+    private ChatClient mChatClient;
+
+    private static final String TAG = "ChatConnection";
+
+    private Socket mSocket;
+    private int mPort = -1;
+
+    public ChatConnection(Handler handler) {
+        mUpdateHandler = handler;
+        mChatServer = new ChatServer(handler);
+    }
+
+    public void tearDown() {
+        mChatServer.tearDown();
+        mChatClient.tearDown();
+    }
+
+    public void connectToServer(InetAddress address, int port) {
+        mChatClient = new ChatClient(address, port);
+    }
+
+    public void sendMessage(String msg) {
+        if (mChatClient != null) {
+            mChatClient.sendMessage(msg);
+        }
+    }
+    
+    public int getLocalPort() {
+        return mPort;
+    }
+    
+    public void setLocalPort(int port) {
+        mPort = port;
+    }
+    
+
+    public synchronized void updateMessages(String msg, boolean local) {
+        Log.e(TAG, "Updating message: " + msg);
+
+        if (local) {
+            msg = "me: " + msg;
+        } else {
+            msg = "them: " + msg;
+        }
+
+        Bundle messageBundle = new Bundle();
+        messageBundle.putString("msg", msg);
+
+        Message message = new Message();
+        message.setData(messageBundle);
+        mUpdateHandler.sendMessage(message);
+
+    }
+
+    private synchronized void setSocket(Socket socket) {
+        Log.d(TAG, "setSocket being called.");
+        if (socket == null) {
+            Log.d(TAG, "Setting a null socket.");
+        }
+        if (mSocket != null) {
+            if (mSocket.isConnected()) {
+                try {
+                    mSocket.close();
+                } catch (IOException e) {
+                    // TODO(alexlucas): Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        }
+        mSocket = socket;
+    }
+
+    private Socket getSocket() {
+        return mSocket;
+    }
+
+    private class ChatServer {
+        ServerSocket mServerSocket = null;
+        Thread mThread = null;
+
+        public ChatServer(Handler handler) {
+            mThread = new Thread(new ServerThread());
+            mThread.start();
+        }
+
+        public void tearDown() {
+            mThread.interrupt();
+            try {
+                mServerSocket.close();
+            } catch (IOException ioe) {
+                Log.e(TAG, "Error when closing server socket.");
+            }
+        }
+
+        class ServerThread implements Runnable {
+
+            @Override
+            public void run() {
+
+                try {
+                    // Since discovery will happen via Nsd, we don't need to care which port is
+                    // used.  Just grab an available one  and advertise it via Nsd.
+                    mServerSocket = new ServerSocket(0);
+                    setLocalPort(mServerSocket.getLocalPort());
+                    
+                    while (!Thread.currentThread().isInterrupted()) {
+                        Log.d(TAG, "ServerSocket Created, awaiting connection");
+                        setSocket(mServerSocket.accept());
+                        Log.d(TAG, "Connected.");
+                        if (mChatClient == null) {
+                            int port = mSocket.getPort();
+                            InetAddress address = mSocket.getInetAddress();
+                            connectToServer(address, port);
+                        }
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "Error creating ServerSocket: ", e);
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private class ChatClient {
+
+        private InetAddress mAddress;
+        private int PORT;
+
+        private final String CLIENT_TAG = "ChatClient";
+
+        private Thread mSendThread;
+        private Thread mRecThread;
+
+        public ChatClient(InetAddress address, int port) {
+
+            Log.d(CLIENT_TAG, "Creating chatClient");
+            this.mAddress = address;
+            this.PORT = port;
+
+            mSendThread = new Thread(new SendingThread());
+            mSendThread.start();
+        }
+
+        class SendingThread implements Runnable {
+
+            BlockingQueue<String> mMessageQueue;
+            private int QUEUE_CAPACITY = 10;
+
+            public SendingThread() {
+                mMessageQueue = new ArrayBlockingQueue<String>(QUEUE_CAPACITY);
+            }
+
+            @Override
+            public void run() {
+                try {
+                    if (getSocket() == null) {
+                        setSocket(new Socket(mAddress, PORT));
+                        Log.d(CLIENT_TAG, "Client-side socket initialized.");
+
+                    } else {
+                        Log.d(CLIENT_TAG, "Socket already initialized. skipping!");
+                    }
+
+                    mRecThread = new Thread(new ReceivingThread());
+                    mRecThread.start();
+
+                } catch (UnknownHostException e) {
+                    Log.d(CLIENT_TAG, "Initializing socket failed, UHE", e);
+                } catch (IOException e) {
+                    Log.d(CLIENT_TAG, "Initializing socket failed, IOE.", e);
+                }
+
+                while (true) {
+                    try {
+                        String msg = mMessageQueue.take();
+                        sendMessage(msg);
+                    } catch (InterruptedException ie) {
+                        Log.d(CLIENT_TAG, "Message sending loop interrupted, exiting");
+                    }
+                }
+            }
+        }
+
+        class ReceivingThread implements Runnable {
+
+            @Override
+            public void run() {
+
+                BufferedReader input;
+                try {
+                    input = new BufferedReader(new InputStreamReader(
+                            mSocket.getInputStream()));
+                    while (!Thread.currentThread().isInterrupted()) {
+
+                        String messageStr = null;
+                        messageStr = input.readLine();
+                        if (messageStr != null) {
+                            Log.d(CLIENT_TAG, "Read from the stream: " + messageStr);
+                            updateMessages(messageStr, false);
+                        } else {
+                            Log.d(CLIENT_TAG, "The nulls! The nulls!");
+                            break;
+                        }
+                    }
+                    input.close();
+
+                } catch (IOException e) {
+                    Log.e(CLIENT_TAG, "Server loop error: ", e);
+                }
+            }
+        }
+
+        public void tearDown() {
+            try {
+                getSocket().close();
+            } catch (IOException ioe) {
+                Log.e(CLIENT_TAG, "Error when closing server socket.");
+            }
+        }
+
+        public void sendMessage(String msg) {
+            try {
+                Socket socket = getSocket();
+                if (socket == null) {
+                    Log.d(CLIENT_TAG, "Socket is null, wtf?");
+                } else if (socket.getOutputStream() == null) {
+                    Log.d(CLIENT_TAG, "Socket output stream is null, wtf?");
+                }
+
+                PrintWriter out = new PrintWriter(
+                        new BufferedWriter(
+                                new OutputStreamWriter(getSocket().getOutputStream())), true);
+                out.println(msg);
+                out.flush();
+                updateMessages(msg, true);
+            } catch (UnknownHostException e) {
+                Log.d(CLIENT_TAG, "Unknown Host", e);
+            } catch (IOException e) {
+                Log.d(CLIENT_TAG, "I/O Exception", e);
+            } catch (Exception e) {
+                Log.d(CLIENT_TAG, "Error3", e);
+            }
+            Log.d(CLIENT_TAG, "Client sent message: " + msg);
+        }
+    }
+}
diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java
new file mode 100644
index 0000000..47ee098
--- /dev/null
+++ b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package com.example.android.nsdchat;
+
+import android.app.Activity;
+import android.net.nsd.NsdServiceInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.example.android.nsdchat.NsdHelper;
+
+public class NsdChatActivity extends Activity {
+
+    NsdHelper mNsdHelper;
+
+    private TextView mStatusView;
+    private Handler mUpdateHandler;
+
+    public static final String TAG = "NsdChat";
+
+    ChatConnection mConnection;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+        mStatusView = (TextView) findViewById(R.id.status);
+
+        mUpdateHandler = new Handler() {
+                @Override
+            public void handleMessage(Message msg) {
+                String chatLine = msg.getData().getString("msg");
+                addChatLine(chatLine);
+            }
+        };
+
+        mConnection = new ChatConnection(mUpdateHandler);
+
+        mNsdHelper = new NsdHelper(this);
+        mNsdHelper.initializeNsd();
+
+    }
+
+    public void clickAdvertise(View v) {
+        // Register service
+        if(mConnection.getLocalPort() > -1) {
+            mNsdHelper.registerService(mConnection.getLocalPort());
+        } else {
+            Log.d(TAG, "ServerSocket isn't bound.");
+        }
+    }
+
+    public void clickDiscover(View v) {
+        mNsdHelper.discoverServices();
+    }
+
+    public void clickConnect(View v) {
+        NsdServiceInfo service = mNsdHelper.getChosenServiceInfo();
+        if (service != null) {
+            Log.d(TAG, "Connecting.");
+            mConnection.connectToServer(service.getHost(),
+                    service.getPort());
+        } else {
+            Log.d(TAG, "No service to connect to!");
+        }
+    }
+
+    public void clickSend(View v) {
+        EditText messageView = (EditText) this.findViewById(R.id.chatInput);
+        if (messageView != null) {
+            String messageString = messageView.getText().toString();
+            if (!messageString.isEmpty()) {
+                mConnection.sendMessage(messageString);
+            }
+            messageView.setText("");
+        }
+    }
+
+    public void addChatLine(String line) {
+        mStatusView.append("\n" + line);
+    }
+
+    @Override
+    protected void onPause() {
+        if (mNsdHelper != null) {
+            mNsdHelper.stopDiscovery();
+        }
+        super.onPause();
+    }
+    
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mNsdHelper != null) {
+            mNsdHelper.discoverServices();
+        }
+    }
+    
+    @Override
+    protected void onDestroy() {
+        mNsdHelper.tearDown();
+        mConnection.tearDown();
+        super.onDestroy();
+    }
+}
diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java
new file mode 100644
index 0000000..568a79b
--- /dev/null
+++ b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package com.example.android.nsdchat;
+
+import android.content.Context;
+import android.net.nsd.NsdServiceInfo;
+import android.net.nsd.NsdManager;
+import android.util.Log;
+
+public class NsdHelper {
+
+    Context mContext;
+
+    NsdManager mNsdManager;
+    NsdManager.ResolveListener mResolveListener;
+    NsdManager.DiscoveryListener mDiscoveryListener;
+    NsdManager.RegistrationListener mRegistrationListener;
+
+    public static final String SERVICE_TYPE = "_http._tcp.";
+
+    public static final String TAG = "NsdHelper";
+    public String mServiceName = "NsdChat";
+
+    NsdServiceInfo mService;
+
+    public NsdHelper(Context context) {
+        mContext = context;
+        mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
+    }
+
+    public void initializeNsd() {
+        initializeResolveListener();
+        initializeDiscoveryListener();
+        initializeRegistrationListener();
+
+        //mNsdManager.init(mContext.getMainLooper(), this);
+
+    }
+
+    public void initializeDiscoveryListener() {
+        mDiscoveryListener = new NsdManager.DiscoveryListener() {
+
+            @Override
+            public void onDiscoveryStarted(String regType) {
+                Log.d(TAG, "Service discovery started");
+            }
+
+            @Override
+            public void onServiceFound(NsdServiceInfo service) {
+                Log.d(TAG, "Service discovery success" + service);
+                if (!service.getServiceType().equals(SERVICE_TYPE)) {
+                    Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
+                } else if (service.getServiceName().equals(mServiceName)) {
+                    Log.d(TAG, "Same machine: " + mServiceName);
+                } else if (service.getServiceName().contains(mServiceName)){
+                    mNsdManager.resolveService(service, mResolveListener);
+                }
+            }
+
+            @Override
+            public void onServiceLost(NsdServiceInfo service) {
+                Log.e(TAG, "service lost" + service);
+                if (mService == service) {
+                    mService = null;
+                }
+            }
+            
+            @Override
+            public void onDiscoveryStopped(String serviceType) {
+                Log.i(TAG, "Discovery stopped: " + serviceType);        
+            }
+
+            @Override
+            public void onStartDiscoveryFailed(String serviceType, int errorCode) {
+                Log.e(TAG, "Discovery failed: Error code:" + errorCode);
+                mNsdManager.stopServiceDiscovery(this);
+            }
+
+            @Override
+            public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+                Log.e(TAG, "Discovery failed: Error code:" + errorCode);
+                mNsdManager.stopServiceDiscovery(this);
+            }
+        };
+    }
+
+    public void initializeResolveListener() {
+        mResolveListener = new NsdManager.ResolveListener() {
+
+            @Override
+            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
+                Log.e(TAG, "Resolve failed" + errorCode);
+            }
+
+            @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;
+            }
+        };
+    }
+
+    public void initializeRegistrationListener() {
+        mRegistrationListener = new NsdManager.RegistrationListener() {
+
+            @Override
+            public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
+                mServiceName = NsdServiceInfo.getServiceName();
+            }
+            
+            @Override
+            public void onRegistrationFailed(NsdServiceInfo arg0, int arg1) {
+            }
+
+            @Override
+            public void onServiceUnregistered(NsdServiceInfo arg0) {
+            }
+            
+            @Override
+            public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            }
+            
+        };
+    }
+
+    public void registerService(int port) {
+        NsdServiceInfo serviceInfo  = new NsdServiceInfo();
+        serviceInfo.setPort(port);
+        serviceInfo.setServiceName(mServiceName);
+        serviceInfo.setServiceType(SERVICE_TYPE);
+        
+        mNsdManager.registerService(
+                serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
+        
+    }
+
+    public void discoverServices() {
+        mNsdManager.discoverServices(
+                SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
+    }
+    
+    public void stopDiscovery() {
+        mNsdManager.stopServiceDiscovery(mDiscoveryListener);
+    }
+
+    public NsdServiceInfo getChosenServiceInfo() {
+        return mService;
+    }
+    
+    public void tearDown() {
+        mNsdManager.unregisterService(mRegistrationListener);
+    }
+}