Mercurial > 510ConnectbotMonitor
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/five_ten_sg/connectbot/monitor/MonitorService.java Mon May 05 13:37:31 2014 -0700 @@ -0,0 +1,384 @@ +package com.five_ten_sg.connectbot.monitor; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Locale; + +import android.app.Activity; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.wifi.WifiManager.WifiLock; +import android.net.wifi.WifiManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.OnInitListener; +import android.util.Log; +import android.widget.TextView; + +public class MonitorService extends Service implements OnInitListener { + public final static String TAG = "ConnectBot.MonitorService"; + + public static final char MONITOR_CMD_INIT = 0; + public static final char MONITOR_CMD_ACTIVATE = 1; + public static final char MONITOR_CMD_HOSTDATA = 2; + public static final char MONITOR_CMD_CURSORMOVE = 3; + public static final char MONITOR_CMD_SCREENCHANGE = 4; + public static final char MONITOR_CMD_FIELDVALUE = 5; + public static final char MONITOR_CMD_SETFIELD = 5; + public static final char MONITOR_CMD_GETFIELD = 6; + public static final char MONITOR_CMD_SCREENWATCH = 7; + + public static final int MONITORPORT = 6000; + + private boolean speech = false; + private TextToSpeech talker = null; + public Handler handler = null; + private ServerSocket serverSocket; + private Thread serverThread = null; + private ConcurrentHashMap<Integer,CommunicationThread> clients = new ConcurrentHashMap<Integer,CommunicationThread>(); + private WifiManager.WifiLock wifiLock; + private PowerManager.WakeLock wakeLock; + final private IBinder binder = new MonitorBinder(); + + + public class MonitorBinder extends Binder { + public MonitorService getService() { + return MonitorService.this; + } + } + + @Override + public void onInit(int status) { + if (status == TextToSpeech.SUCCESS) { + talker.setLanguage(Locale.US); + speech = true; + } + } + + @Override + public void onCreate() { + WifiManager wMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE); + wifiLock = wMgr.createWifiLock(WifiManager.WIFI_MODE_FULL, "MyWifiLock"); + wifiLock.acquire(); + + PowerManager pMgr = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = pMgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock"); + wakeLock.acquire(); + + talker = new TextToSpeech(this, this); + this.serverThread = new Thread(new ServerThread()); + this.serverThread.start(); + } + + @Override + public IBinder onBind(Intent intent) { + startService(new Intent(this, MonitorService.class)); + return binder; + } + + public void printer(String msg) { + if (handler != null) handler.sendMessage(handler.obtainMessage(MonitorActivity.MESSAGE_CODE_PRINT, msg)); + if (speech) talker.speak(msg, TextToSpeech.QUEUE_FLUSH, null); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(TAG, "service onStartCommand()"); + return START_STICKY; + } + + @Override + public void onDestroy() { + try { + Log.i(TAG, "service onDestroy()"); + talker.stop(); + talker.shutdown(); + wifiLock.release(); + wakeLock.release(); + serverSocket.close(); + } catch (IOException e) { + Log.e(TAG, "exception in onDestroy()", e); + } + super.onDestroy(); + } + + class ServerThread extends Thread { + public void run() { + Socket socket = null; + int connection = 0; + try { + serverSocket = new ServerSocket(MONITORPORT); + } catch (IOException e) { + Log.e(TAG, "exception in ServerThread.run(), cannot create listening socket", e); + return; + } + while (true) { + try{ + socket = serverSocket.accept(); + connection = connection + 1; + CommunicationThread commThread = new CommunicationThread(connection, socket); + clients.put(connection, commThread); + commThread.start(); + } catch (IOException e) { + Log.e(TAG, "exception in ServerThread.run(), listening socket closed", e); + break; + } + } + } + } + + class triple { + private int l, c; + private char[] b; + public triple(int l, int c, char[] b) { + this.l = l; + this.c = c; + this.b = b; + } + } + + class CommunicationThread extends Thread { + public int connection; + private Socket client_socket; + private InputStream client_in; + private OutputStream client_out; + private boolean is_closing = false; + private BlockingQueue<triple> queue = new ArrayBlockingQueue<triple>(1); + + public CommunicationThread(int handle, Socket socket) { + connection = handle; + client_socket = socket; + try { + client_in = client_socket.getInputStream(); + client_out = client_socket.getOutputStream(); + } catch (IOException e) { + Log.e(TAG, "exception in CommunicationThread() constructor, cannot get socket streams", e); + } + } + + public char[] bytesToChars(byte[] b, int len) { + char[] c = new char[len >> 1]; + int bp = 0; + for(int i = 0; i < c.length; i++) { + byte b1 = b[bp++]; + byte b2 = b[bp++]; + c[i] = (char) (((b1 & 0x00FF) << 8) + (b2 & 0x00FF)); + } + return c; + } + + public byte[] charsToBytes(char[] c) { + byte[] b = new byte[c.length << 1]; + int bp = 0; + for (int i=0; i<c.length; i++) { + b[bp++] = (byte) ((c[i] & 0xff00) >> 8); + b[bp++] = (byte) (c[i] & 0x00ff); + } + return b; + } + + public synchronized void clientWrite(char cmd, char[] c) { + try { + if (client_out != null) { + c[0] = (char)(c.length - 1); // number of chars following + c[1] = cmd; + Log.i(TAG, String.format("sending %d command", (int)cmd)); + client_out.write(charsToBytes(c)); + } + } + catch (IOException e) { + Log.e(TAG, "exception in monitorWrite()", e); + try { + client_out.close(); + } + catch (IOException ee) { + Log.e(TAG, "exception in monitorWrite() closing socket", ee); + } + client_out = null; + } + }; + + private char[] forceRead(int len) throws IOException { + int len2 = len*2; + int off = 0; + byte[] b = new byte[len2]; + while (off < len2) { + int l = client_in.read(b, off, len2-off); + if (l < 0) { + is_closing = true; + throw new IOException("eof"); + } + off += l; + } + return bytesToChars(b, len2); + } + + public void run() { + Log.i(TAG, String.format("CommunicationThread.run() client %d connected", connection)); + while (true) { + try { + char[] len = forceRead(1); + char[] packet = forceRead(len[0]); + char[] buf; + char cmd = packet[0]; + int plen = packet.length; + //Log.i(TAG, String.format("received %d command length %d", (int)cmd, plen)); + switch (cmd) { + case MONITOR_CMD_INIT: + buf = new char[plen-1]; + System.arraycopy(packet, 1, buf, 0, plen-1); + abandonGetField(connection); + teInit(connection, buf); + break; + case MONITOR_CMD_ACTIVATE: + abandonGetField(connection); + buf = new char[plen-3]; + System.arraycopy(packet, 3, buf, 0, plen-3); + teActivate(connection, packet[1], packet[2], buf); + break; + case MONITOR_CMD_HOSTDATA: + teHostData(connection, packet[1]); + break; + case MONITOR_CMD_CURSORMOVE: + teCursorMove(connection, packet[1], packet[2]); + break; + case MONITOR_CMD_SCREENCHANGE: + buf = new char[plen-3]; + System.arraycopy(packet, 3, buf, 0, plen-3); + teScreenChange(connection, packet[1], packet[2], buf); + break; + case MONITOR_CMD_FIELDVALUE: + buf = new char[plen-3]; + System.arraycopy(packet, 3, buf, 0, plen-3); + Log.i(TAG, String.format("teFieldValue %d line %d column %d b.len %d", connection, packet[1], packet[2], buf.length)); + queue.put(new triple(packet[1], packet[2], buf)); + break; + default: + break; + } + } catch (IOException e) { + if (!is_closing) Log.e(TAG, "exception in CommunicationThread.run()", e); + break; + } catch (InterruptedException e) { + Log.e(TAG, "exception in CommunicationThread.run()", e); + break; + } + } + Log.i(TAG, String.format("shutting down connection %d", connection)); + try { + if (client_in != null) client_in.close(); + if (client_out != null) client_out.close(); + if (client_socket != null) client_socket.close(); + } catch (IOException e) { + Log.e(TAG, "exception in CommunicationThread.run() closing sockets", e); + } + client_in = null; + client_out = null; + client_socket = null; + } + } + + private void abandonGetField(int except) { + for (CommunicationThread cm : clients.values()) { + if (cm.connection != except) { + cm.queue.offer(new triple(0, 0, new char[0])); + } + } + } + + + //////////////////////////////////////// + //// these functions run on the reader thread here and call your monitoring code + + public void teInit(int connection, char[] buf) { + String fn = new String(buf); + Log.i(TAG, String.format("teInit %d file %s", connection, fn)); + printer(String.format("init %d %s", connection, fn)); + } + + public void teActivate(int connection, int lines, int columns, char[] buf) { + Log.i(TAG, String.format("teActivate %d", connection)); + printer(String.format("activate %d lines %d columns %d b.len %d", connection, lines, columns, buf.length)); + } + + public void teHostData(int connection, int keyCode) { + Log.i(TAG, String.format("teHostData %d key %d", connection, keyCode)); + printer(String.format("key %d is %d", connection, keyCode)); + } + + public void teCursorMove(int connection, int l, int c) { + //Log.i(TAG, String.format("teCursorMove %d line %d column %d", connection, l, c)); + } + + public void teScreenChange(int connection, int lines, int columns, char[] buf) { + Log.i(TAG, String.format("teScreenChange %d lines %d columns %d b.len %d", connection, lines, columns, buf.length)); + } + + + //////////////////////////////////////// + //// these functions are called from your monitoring code thread + + public void teSetField(int connection, int l, int c, char[] buf) { + int len = buf.length; + Log.i(TAG, String.format("teSetField %d request line %d column %d len %d", connection, l, c, len)); + CommunicationThread cm = clients.get(connection); + if (cm != null) { + char[] arg2 = new char[4 + len]; + arg2[2] = (char) (l & 0x0000ffff); + arg2[3] = (char) (c & 0x0000ffff); + int base = 4; + System.arraycopy(buf, 0, arg2, base, len); + cm.clientWrite(MONITOR_CMD_SETFIELD, arg2); + } + } + + public char[] teGetField(int connection, int l, int c, int len) { + Log.i(TAG, String.format("teGetField %d request line %d column %d len %d", connection, l, c, len)); + CommunicationThread cm = clients.get(connection); + if (cm != null) { + char[] arg = new char[5]; + arg[2] = (char) (l & 0x0000ffff); + arg[3] = (char) (c & 0x0000ffff); + arg[4] = (char) (len & 0x0000ffff); + cm.queue.clear(); // we never have more than one outstanding getfield request on the connection + cm.clientWrite(MONITOR_CMD_GETFIELD, arg); + try { + triple t = cm.queue.take(); // wait for response + Log.i(TAG, String.format("teGetField %d response line %d column %d len %d", connection, t.l, t.c, t.b.length)); + return t.b; + } catch (InterruptedException e) { + Log.e(TAG, "exception in teGetField(), return empty string", e); + } + } + return new char[0]; + } + + public void teScreenWatch(int connection, int l, int c, int len) { + Log.i(TAG, String.format("teScreenWatch %d request line %d column %d len %d", connection, l, c, len)); + CommunicationThread cm = clients.get(connection); + if (cm != null) { + char[] arg = new char[5]; + arg[2] = (char) (l & 0x0000ffff); + arg[3] = (char) (c & 0x0000ffff); + arg[4] = (char) (len & 0x0000ffff); + cm.clientWrite(MONITOR_CMD_GETFIELD, arg); + } + } + + public void teSpeak(int connection, String msg, boolean flush) { + if (speech) talker.speak(msg, (flush) ? TextToSpeech.QUEUE_FLUSH : TextToSpeech.QUEUE_ADD, null); + } +}