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