0
|
1 package com.five_ten_sg.connectbot.monitor;
|
|
2
|
|
3 import java.io.IOException;
|
|
4 import java.io.InputStream;
|
|
5 import java.io.OutputStream;
|
|
6 import java.net.ServerSocket;
|
|
7 import java.net.Socket;
|
|
8 import java.util.concurrent.ArrayBlockingQueue;
|
|
9 import java.util.concurrent.BlockingQueue;
|
|
10 import java.util.concurrent.ConcurrentHashMap;
|
|
11 import java.util.Locale;
|
|
12
|
|
13 import android.app.Activity;
|
|
14 import android.app.Service;
|
|
15 import android.content.Context;
|
|
16 import android.content.Intent;
|
|
17 import android.content.ServiceConnection;
|
|
18 import android.net.wifi.WifiManager.WifiLock;
|
|
19 import android.net.wifi.WifiManager;
|
|
20 import android.os.Binder;
|
|
21 import android.os.Bundle;
|
|
22 import android.os.Handler;
|
|
23 import android.os.IBinder;
|
|
24 import android.os.Message;
|
|
25 import android.os.PowerManager;
|
|
26 import android.speech.tts.TextToSpeech;
|
|
27 import android.speech.tts.TextToSpeech.OnInitListener;
|
|
28 import android.util.Log;
|
|
29 import android.widget.TextView;
|
|
30
|
|
31 public class MonitorService extends Service implements OnInitListener {
|
|
32 public final static String TAG = "ConnectBot.MonitorService";
|
|
33
|
|
34 public static final char MONITOR_CMD_INIT = 0;
|
|
35 public static final char MONITOR_CMD_ACTIVATE = 1;
|
|
36 public static final char MONITOR_CMD_HOSTDATA = 2;
|
|
37 public static final char MONITOR_CMD_CURSORMOVE = 3;
|
|
38 public static final char MONITOR_CMD_SCREENCHANGE = 4;
|
|
39 public static final char MONITOR_CMD_FIELDVALUE = 5;
|
|
40 public static final char MONITOR_CMD_SETFIELD = 5;
|
|
41 public static final char MONITOR_CMD_GETFIELD = 6;
|
|
42 public static final char MONITOR_CMD_SCREENWATCH = 7;
|
|
43
|
|
44 public static final int MONITORPORT = 6000;
|
|
45
|
|
46 private boolean speech = false;
|
|
47 private TextToSpeech talker = null;
|
|
48 public Handler handler = null;
|
|
49 private ServerSocket serverSocket;
|
|
50 private Thread serverThread = null;
|
|
51 private ConcurrentHashMap<Integer,CommunicationThread> clients = new ConcurrentHashMap<Integer,CommunicationThread>();
|
|
52 private WifiManager.WifiLock wifiLock;
|
|
53 private PowerManager.WakeLock wakeLock;
|
|
54 final private IBinder binder = new MonitorBinder();
|
|
55
|
|
56
|
|
57 public class MonitorBinder extends Binder {
|
|
58 public MonitorService getService() {
|
|
59 return MonitorService.this;
|
|
60 }
|
|
61 }
|
|
62
|
|
63 @Override
|
|
64 public void onInit(int status) {
|
|
65 if (status == TextToSpeech.SUCCESS) {
|
|
66 talker.setLanguage(Locale.US);
|
|
67 speech = true;
|
|
68 }
|
|
69 }
|
|
70
|
|
71 @Override
|
|
72 public void onCreate() {
|
|
73 WifiManager wMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE);
|
|
74 wifiLock = wMgr.createWifiLock(WifiManager.WIFI_MODE_FULL, "MyWifiLock");
|
|
75 wifiLock.acquire();
|
|
76
|
|
77 PowerManager pMgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
|
78 wakeLock = pMgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
|
|
79 wakeLock.acquire();
|
|
80
|
|
81 talker = new TextToSpeech(this, this);
|
|
82 this.serverThread = new Thread(new ServerThread());
|
|
83 this.serverThread.start();
|
|
84 }
|
|
85
|
|
86 @Override
|
|
87 public IBinder onBind(Intent intent) {
|
|
88 startService(new Intent(this, MonitorService.class));
|
|
89 return binder;
|
|
90 }
|
|
91
|
|
92 public void printer(String msg) {
|
|
93 if (handler != null) handler.sendMessage(handler.obtainMessage(MonitorActivity.MESSAGE_CODE_PRINT, msg));
|
|
94 if (speech) talker.speak(msg, TextToSpeech.QUEUE_FLUSH, null);
|
|
95 }
|
|
96
|
|
97 @Override
|
|
98 public int onStartCommand(Intent intent, int flags, int startId) {
|
|
99 Log.i(TAG, "service onStartCommand()");
|
|
100 return START_STICKY;
|
|
101 }
|
|
102
|
|
103 @Override
|
|
104 public void onDestroy() {
|
|
105 try {
|
|
106 Log.i(TAG, "service onDestroy()");
|
|
107 talker.stop();
|
|
108 talker.shutdown();
|
|
109 wifiLock.release();
|
|
110 wakeLock.release();
|
|
111 serverSocket.close();
|
|
112 } catch (IOException e) {
|
|
113 Log.e(TAG, "exception in onDestroy()", e);
|
|
114 }
|
|
115 super.onDestroy();
|
|
116 }
|
|
117
|
|
118 class ServerThread extends Thread {
|
|
119 public void run() {
|
|
120 Socket socket = null;
|
|
121 int connection = 0;
|
|
122 try {
|
|
123 serverSocket = new ServerSocket(MONITORPORT);
|
|
124 } catch (IOException e) {
|
|
125 Log.e(TAG, "exception in ServerThread.run(), cannot create listening socket", e);
|
|
126 return;
|
|
127 }
|
|
128 while (true) {
|
|
129 try{
|
|
130 socket = serverSocket.accept();
|
|
131 connection = connection + 1;
|
|
132 CommunicationThread commThread = new CommunicationThread(connection, socket);
|
|
133 clients.put(connection, commThread);
|
|
134 commThread.start();
|
|
135 } catch (IOException e) {
|
|
136 Log.e(TAG, "exception in ServerThread.run(), listening socket closed", e);
|
|
137 break;
|
|
138 }
|
|
139 }
|
|
140 }
|
|
141 }
|
|
142
|
|
143 class triple {
|
|
144 private int l, c;
|
|
145 private char[] b;
|
|
146 public triple(int l, int c, char[] b) {
|
|
147 this.l = l;
|
|
148 this.c = c;
|
|
149 this.b = b;
|
|
150 }
|
|
151 }
|
|
152
|
|
153 class CommunicationThread extends Thread {
|
|
154 public int connection;
|
|
155 private Socket client_socket;
|
|
156 private InputStream client_in;
|
|
157 private OutputStream client_out;
|
|
158 private boolean is_closing = false;
|
|
159 private BlockingQueue<triple> queue = new ArrayBlockingQueue<triple>(1);
|
|
160
|
|
161 public CommunicationThread(int handle, Socket socket) {
|
|
162 connection = handle;
|
|
163 client_socket = socket;
|
|
164 try {
|
|
165 client_in = client_socket.getInputStream();
|
|
166 client_out = client_socket.getOutputStream();
|
|
167 } catch (IOException e) {
|
|
168 Log.e(TAG, "exception in CommunicationThread() constructor, cannot get socket streams", e);
|
|
169 }
|
|
170 }
|
|
171
|
|
172 public char[] bytesToChars(byte[] b, int len) {
|
|
173 char[] c = new char[len >> 1];
|
|
174 int bp = 0;
|
|
175 for(int i = 0; i < c.length; i++) {
|
|
176 byte b1 = b[bp++];
|
|
177 byte b2 = b[bp++];
|
|
178 c[i] = (char) (((b1 & 0x00FF) << 8) + (b2 & 0x00FF));
|
|
179 }
|
|
180 return c;
|
|
181 }
|
|
182
|
|
183 public byte[] charsToBytes(char[] c) {
|
|
184 byte[] b = new byte[c.length << 1];
|
|
185 int bp = 0;
|
|
186 for (int i=0; i<c.length; i++) {
|
|
187 b[bp++] = (byte) ((c[i] & 0xff00) >> 8);
|
|
188 b[bp++] = (byte) (c[i] & 0x00ff);
|
|
189 }
|
|
190 return b;
|
|
191 }
|
|
192
|
|
193 public synchronized void clientWrite(char cmd, char[] c) {
|
|
194 try {
|
|
195 if (client_out != null) {
|
|
196 c[0] = (char)(c.length - 1); // number of chars following
|
|
197 c[1] = cmd;
|
|
198 Log.i(TAG, String.format("sending %d command", (int)cmd));
|
|
199 client_out.write(charsToBytes(c));
|
|
200 }
|
|
201 }
|
|
202 catch (IOException e) {
|
|
203 Log.e(TAG, "exception in monitorWrite()", e);
|
|
204 try {
|
|
205 client_out.close();
|
|
206 }
|
|
207 catch (IOException ee) {
|
|
208 Log.e(TAG, "exception in monitorWrite() closing socket", ee);
|
|
209 }
|
|
210 client_out = null;
|
|
211 }
|
|
212 };
|
|
213
|
|
214 private char[] forceRead(int len) throws IOException {
|
|
215 int len2 = len*2;
|
|
216 int off = 0;
|
|
217 byte[] b = new byte[len2];
|
|
218 while (off < len2) {
|
|
219 int l = client_in.read(b, off, len2-off);
|
|
220 if (l < 0) {
|
|
221 is_closing = true;
|
|
222 throw new IOException("eof");
|
|
223 }
|
|
224 off += l;
|
|
225 }
|
|
226 return bytesToChars(b, len2);
|
|
227 }
|
|
228
|
|
229 public void run() {
|
|
230 Log.i(TAG, String.format("CommunicationThread.run() client %d connected", connection));
|
|
231 while (true) {
|
|
232 try {
|
|
233 char[] len = forceRead(1);
|
|
234 char[] packet = forceRead(len[0]);
|
|
235 char[] buf;
|
|
236 char cmd = packet[0];
|
|
237 int plen = packet.length;
|
|
238 //Log.i(TAG, String.format("received %d command length %d", (int)cmd, plen));
|
|
239 switch (cmd) {
|
|
240 case MONITOR_CMD_INIT:
|
|
241 buf = new char[plen-1];
|
|
242 System.arraycopy(packet, 1, buf, 0, plen-1);
|
|
243 abandonGetField(connection);
|
|
244 teInit(connection, buf);
|
|
245 break;
|
|
246 case MONITOR_CMD_ACTIVATE:
|
|
247 abandonGetField(connection);
|
|
248 buf = new char[plen-3];
|
|
249 System.arraycopy(packet, 3, buf, 0, plen-3);
|
|
250 teActivate(connection, packet[1], packet[2], buf);
|
|
251 break;
|
|
252 case MONITOR_CMD_HOSTDATA:
|
|
253 teHostData(connection, packet[1]);
|
|
254 break;
|
|
255 case MONITOR_CMD_CURSORMOVE:
|
|
256 teCursorMove(connection, packet[1], packet[2]);
|
|
257 break;
|
|
258 case MONITOR_CMD_SCREENCHANGE:
|
|
259 buf = new char[plen-3];
|
|
260 System.arraycopy(packet, 3, buf, 0, plen-3);
|
|
261 teScreenChange(connection, packet[1], packet[2], buf);
|
|
262 break;
|
|
263 case MONITOR_CMD_FIELDVALUE:
|
|
264 buf = new char[plen-3];
|
|
265 System.arraycopy(packet, 3, buf, 0, plen-3);
|
|
266 Log.i(TAG, String.format("teFieldValue %d line %d column %d b.len %d", connection, packet[1], packet[2], buf.length));
|
|
267 queue.put(new triple(packet[1], packet[2], buf));
|
|
268 break;
|
|
269 default:
|
|
270 break;
|
|
271 }
|
|
272 } catch (IOException e) {
|
|
273 if (!is_closing) Log.e(TAG, "exception in CommunicationThread.run()", e);
|
|
274 break;
|
|
275 } catch (InterruptedException e) {
|
|
276 Log.e(TAG, "exception in CommunicationThread.run()", e);
|
|
277 break;
|
|
278 }
|
|
279 }
|
|
280 Log.i(TAG, String.format("shutting down connection %d", connection));
|
|
281 try {
|
|
282 if (client_in != null) client_in.close();
|
|
283 if (client_out != null) client_out.close();
|
|
284 if (client_socket != null) client_socket.close();
|
|
285 } catch (IOException e) {
|
|
286 Log.e(TAG, "exception in CommunicationThread.run() closing sockets", e);
|
|
287 }
|
|
288 client_in = null;
|
|
289 client_out = null;
|
|
290 client_socket = null;
|
|
291 }
|
|
292 }
|
|
293
|
|
294 private void abandonGetField(int except) {
|
|
295 for (CommunicationThread cm : clients.values()) {
|
|
296 if (cm.connection != except) {
|
|
297 cm.queue.offer(new triple(0, 0, new char[0]));
|
|
298 }
|
|
299 }
|
|
300 }
|
|
301
|
|
302
|
|
303 ////////////////////////////////////////
|
|
304 //// these functions run on the reader thread here and call your monitoring code
|
|
305
|
|
306 public void teInit(int connection, char[] buf) {
|
|
307 String fn = new String(buf);
|
|
308 Log.i(TAG, String.format("teInit %d file %s", connection, fn));
|
|
309 printer(String.format("init %d %s", connection, fn));
|
|
310 }
|
|
311
|
|
312 public void teActivate(int connection, int lines, int columns, char[] buf) {
|
|
313 Log.i(TAG, String.format("teActivate %d", connection));
|
|
314 printer(String.format("activate %d lines %d columns %d b.len %d", connection, lines, columns, buf.length));
|
|
315 }
|
|
316
|
|
317 public void teHostData(int connection, int keyCode) {
|
|
318 Log.i(TAG, String.format("teHostData %d key %d", connection, keyCode));
|
|
319 printer(String.format("key %d is %d", connection, keyCode));
|
|
320 }
|
|
321
|
|
322 public void teCursorMove(int connection, int l, int c) {
|
|
323 //Log.i(TAG, String.format("teCursorMove %d line %d column %d", connection, l, c));
|
|
324 }
|
|
325
|
|
326 public void teScreenChange(int connection, int lines, int columns, char[] buf) {
|
|
327 Log.i(TAG, String.format("teScreenChange %d lines %d columns %d b.len %d", connection, lines, columns, buf.length));
|
|
328 }
|
|
329
|
|
330
|
|
331 ////////////////////////////////////////
|
|
332 //// these functions are called from your monitoring code thread
|
|
333
|
|
334 public void teSetField(int connection, int l, int c, char[] buf) {
|
|
335 int len = buf.length;
|
|
336 Log.i(TAG, String.format("teSetField %d request line %d column %d len %d", connection, l, c, len));
|
|
337 CommunicationThread cm = clients.get(connection);
|
|
338 if (cm != null) {
|
|
339 char[] arg2 = new char[4 + len];
|
|
340 arg2[2] = (char) (l & 0x0000ffff);
|
|
341 arg2[3] = (char) (c & 0x0000ffff);
|
|
342 int base = 4;
|
|
343 System.arraycopy(buf, 0, arg2, base, len);
|
|
344 cm.clientWrite(MONITOR_CMD_SETFIELD, arg2);
|
|
345 }
|
|
346 }
|
|
347
|
|
348 public char[] teGetField(int connection, int l, int c, int len) {
|
|
349 Log.i(TAG, String.format("teGetField %d request line %d column %d len %d", connection, l, c, len));
|
|
350 CommunicationThread cm = clients.get(connection);
|
|
351 if (cm != null) {
|
|
352 char[] arg = new char[5];
|
|
353 arg[2] = (char) (l & 0x0000ffff);
|
|
354 arg[3] = (char) (c & 0x0000ffff);
|
|
355 arg[4] = (char) (len & 0x0000ffff);
|
|
356 cm.queue.clear(); // we never have more than one outstanding getfield request on the connection
|
|
357 cm.clientWrite(MONITOR_CMD_GETFIELD, arg);
|
|
358 try {
|
|
359 triple t = cm.queue.take(); // wait for response
|
|
360 Log.i(TAG, String.format("teGetField %d response line %d column %d len %d", connection, t.l, t.c, t.b.length));
|
|
361 return t.b;
|
|
362 } catch (InterruptedException e) {
|
|
363 Log.e(TAG, "exception in teGetField(), return empty string", e);
|
|
364 }
|
|
365 }
|
|
366 return new char[0];
|
|
367 }
|
|
368
|
|
369 public void teScreenWatch(int connection, int l, int c, int len) {
|
|
370 Log.i(TAG, String.format("teScreenWatch %d request line %d column %d len %d", connection, l, c, len));
|
|
371 CommunicationThread cm = clients.get(connection);
|
|
372 if (cm != null) {
|
|
373 char[] arg = new char[5];
|
|
374 arg[2] = (char) (l & 0x0000ffff);
|
|
375 arg[3] = (char) (c & 0x0000ffff);
|
|
376 arg[4] = (char) (len & 0x0000ffff);
|
|
377 cm.clientWrite(MONITOR_CMD_GETFIELD, arg);
|
|
378 }
|
|
379 }
|
|
380
|
|
381 public void teSpeak(int connection, String msg, boolean flush) {
|
|
382 if (speech) talker.speak(msg, (flush) ? TextToSpeech.QUEUE_FLUSH : TextToSpeech.QUEUE_ADD, null);
|
|
383 }
|
|
384 }
|