1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.nsdchat; 18 19 import android.os.Bundle; 20 import android.os.Handler; 21 import android.os.Message; 22 import android.util.Log; 23 24 import java.io.BufferedReader; 25 import java.io.BufferedWriter; 26 import java.io.IOException; 27 import java.io.InputStreamReader; 28 import java.io.OutputStreamWriter; 29 import java.io.PrintWriter; 30 import java.net.InetAddress; 31 import java.net.ServerSocket; 32 import java.net.Socket; 33 import java.net.UnknownHostException; 34 import java.util.concurrent.ArrayBlockingQueue; 35 import java.util.concurrent.BlockingQueue; 36 37 public class ChatConnection { 38 39 private Handler mUpdateHandler; 40 private ChatServer mChatServer; 41 private ChatClient mChatClient; 42 43 private static final String TAG = "ChatConnection"; 44 45 private Socket mSocket; 46 private int mPort = -1; 47 48 public ChatConnection(Handler handler) { 49 mUpdateHandler = handler; 50 mChatServer = new ChatServer(handler); 51 } 52 53 public void tearDown() { 54 mChatServer.tearDown(); 55 if (mChatClient != null) { 56 mChatClient.tearDown(); 57 } 58 } 59 60 public void connectToServer(InetAddress address, int port) { 61 mChatClient = new ChatClient(address, port); 62 } 63 64 public void sendMessage(String msg) { 65 if (mChatClient != null) { 66 mChatClient.sendMessage(msg); 67 } 68 } 69 70 public int getLocalPort() { 71 return mPort; 72 } 73 74 public void setLocalPort(int port) { 75 mPort = port; 76 } 77 78 79 public synchronized void updateMessages(String msg, boolean local) { 80 Log.e(TAG, "Updating message: " + msg); 81 82 if (local) { 83 msg = "me: " + msg; 84 } else { 85 msg = "them: " + msg; 86 } 87 88 Bundle messageBundle = new Bundle(); 89 messageBundle.putString("msg", msg); 90 91 Message message = new Message(); 92 message.setData(messageBundle); 93 mUpdateHandler.sendMessage(message); 94 95 } 96 97 private synchronized void setSocket(Socket socket) { 98 Log.d(TAG, "setSocket being called."); 99 if (socket == null) { 100 Log.d(TAG, "Setting a null socket."); 101 } 102 if (mSocket != null) { 103 if (mSocket.isConnected()) { 104 try { 105 mSocket.close(); 106 } catch (IOException e) { 107 // TODO(alexlucas): Auto-generated catch block 108 e.printStackTrace(); 109 } 110 } 111 } 112 mSocket = socket; 113 } 114 115 private Socket getSocket() { 116 return mSocket; 117 } 118 119 private class ChatServer { 120 ServerSocket mServerSocket = null; 121 Thread mThread = null; 122 123 public ChatServer(Handler handler) { 124 mThread = new Thread(new ServerThread()); 125 mThread.start(); 126 } 127 128 public void tearDown() { 129 mThread.interrupt(); 130 try { 131 mServerSocket.close(); 132 } catch (IOException ioe) { 133 Log.e(TAG, "Error when closing server socket."); 134 } 135 } 136 137 class ServerThread implements Runnable { 138 139 @Override 140 public void run() { 141 142 try { 143 // Since discovery will happen via Nsd, we don't need to care which port is 144 // used. Just grab an available one and advertise it via Nsd. 145 mServerSocket = new ServerSocket(0); 146 setLocalPort(mServerSocket.getLocalPort()); 147 148 while (!Thread.currentThread().isInterrupted()) { 149 Log.d(TAG, "ServerSocket Created, awaiting connection"); 150 setSocket(mServerSocket.accept()); 151 Log.d(TAG, "Connected."); 152 if (mChatClient == null) { 153 int port = mSocket.getPort(); 154 InetAddress address = mSocket.getInetAddress(); 155 connectToServer(address, port); 156 } 157 } 158 } catch (IOException e) { 159 Log.e(TAG, "Error creating ServerSocket: ", e); 160 e.printStackTrace(); 161 } 162 } 163 } 164 } 165 166 private class ChatClient { 167 168 private InetAddress mAddress; 169 private int PORT; 170 171 private final String CLIENT_TAG = "ChatClient"; 172 173 private Thread mSendThread; 174 private Thread mRecThread; 175 176 public ChatClient(InetAddress address, int port) { 177 178 Log.d(CLIENT_TAG, "Creating chatClient"); 179 this.mAddress = address; 180 this.PORT = port; 181 182 mSendThread = new Thread(new SendingThread()); 183 mSendThread.start(); 184 } 185 186 class SendingThread implements Runnable { 187 188 BlockingQueue<String> mMessageQueue; 189 private int QUEUE_CAPACITY = 10; 190 191 public SendingThread() { 192 mMessageQueue = new ArrayBlockingQueue<String>(QUEUE_CAPACITY); 193 } 194 195 @Override 196 public void run() { 197 try { 198 if (getSocket() == null) { 199 setSocket(new Socket(mAddress, PORT)); 200 Log.d(CLIENT_TAG, "Client-side socket initialized."); 201 202 } else { 203 Log.d(CLIENT_TAG, "Socket already initialized. skipping!"); 204 } 205 206 mRecThread = new Thread(new ReceivingThread()); 207 mRecThread.start(); 208 209 } catch (UnknownHostException e) { 210 Log.d(CLIENT_TAG, "Initializing socket failed, UHE", e); 211 } catch (IOException e) { 212 Log.d(CLIENT_TAG, "Initializing socket failed, IOE.", e); 213 } 214 215 while (true) { 216 try { 217 String msg = mMessageQueue.take(); 218 sendMessage(msg); 219 } catch (InterruptedException ie) { 220 Log.d(CLIENT_TAG, "Message sending loop interrupted, exiting"); 221 } 222 } 223 } 224 } 225 226 class ReceivingThread implements Runnable { 227 228 @Override 229 public void run() { 230 231 BufferedReader input; 232 try { 233 input = new BufferedReader(new InputStreamReader( 234 mSocket.getInputStream())); 235 while (!Thread.currentThread().isInterrupted()) { 236 237 String messageStr = null; 238 messageStr = input.readLine(); 239 if (messageStr != null) { 240 Log.d(CLIENT_TAG, "Read from the stream: " + messageStr); 241 updateMessages(messageStr, false); 242 } else { 243 Log.d(CLIENT_TAG, "The nulls! The nulls!"); 244 break; 245 } 246 } 247 input.close(); 248 249 } catch (IOException e) { 250 Log.e(CLIENT_TAG, "Server loop error: ", e); 251 } 252 } 253 } 254 255 public void tearDown() { 256 try { 257 getSocket().close(); 258 } catch (IOException ioe) { 259 Log.e(CLIENT_TAG, "Error when closing server socket."); 260 } 261 } 262 263 public void sendMessage(String msg) { 264 try { 265 Socket socket = getSocket(); 266 if (socket == null) { 267 Log.d(CLIENT_TAG, "Socket is null, wtf?"); 268 } else if (socket.getOutputStream() == null) { 269 Log.d(CLIENT_TAG, "Socket output stream is null, wtf?"); 270 } 271 272 PrintWriter out = new PrintWriter( 273 new BufferedWriter( 274 new OutputStreamWriter(getSocket().getOutputStream())), true); 275 out.println(msg); 276 out.flush(); 277 updateMessages(msg, true); 278 } catch (UnknownHostException e) { 279 Log.d(CLIENT_TAG, "Unknown Host", e); 280 } catch (IOException e) { 281 Log.d(CLIENT_TAG, "I/O Exception", e); 282 } catch (Exception e) { 283 Log.d(CLIENT_TAG, "Error3", e); 284 } 285 Log.d(CLIENT_TAG, "Client sent message: " + msg); 286 } 287 } 288 } 289