Mercurial > 510ConnectbotMonitor
comparison src/com/five_ten_sg/connectbot/monitor/MonitorService.java @ 0:5564580fe160
initial version
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Mon, 05 May 2014 13:37:31 -0700 |
parents | |
children | f6a1aabf384f |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:5564580fe160 |
---|---|
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 } |