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