Mercurial > 510ConnectbotMonitor
view src/com/five_ten_sg/connectbot/monitor/MonitorService.java @ 14:0ccc5139199c
Added tag stable-1.0.1 for changeset 5bf6d84cc5b8
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Tue, 24 Jun 2014 08:06:00 -0700 |
parents | 5bf6d84cc5b8 |
children | a481d8fb5571 |
line wrap: on
line source
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.HashMap; 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_KEYSTATE = 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 char MONITOR_CMD_DEPRESS = 8; public static final char MONITOR_CMD_SHOWURL = 9; public static final int MONITORPORT = 6000; public static ConcurrentHashMap<Integer,CommunicationThread> clients = new ConcurrentHashMap<Integer,CommunicationThread>(); public static int currentConnection = -1; private boolean speech = false; private TextToSpeech talker = null; private BlockingQueue<String> talkerQueue = null; public Handler handler = null; private ServerSocket serverSocket; private Thread serverThread = null; 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)); } @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 void speak(byte [] msg, boolean flush, boolean synchronous) { if (speech) { String smsg = bytesToString(msg); if (synchronous) { HashMap<String, String> myHashParms = new HashMap(); myHashParms.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, String.format("connection %d", connection)); talker.speak(smsg, (flush) ? TextToSpeech.QUEUE_FLUSH : TextToSpeech.QUEUE_ADD, myHashParms); try { String x = talkerQueue.take(); // wait for completion } catch (InterruptedException e) { Log.e(TAG, "exception in cm.speak()", e); } } else { talker.speak(smsg, (flush) ? TextToSpeech.QUEUE_FLUSH : TextToSpeech.QUEUE_ADD, null); } } } public String bytesToString(byte[] b) { char[] c = new char[b.length]; int bp = 0; for(int i = 0; i < c.length; i++) { byte b1 = 0; byte b2 = b[bp++]; c[i] = (char) (((b1 & 0x00FF) << 8) + (b2 & 0x00FF)); } return new String(c); } 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_KEYSTATE: teKeyState(connection, (packet[1] == 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 teKeyState(int connection, boolean down) { String d = (down) ? "yes" : "no"; Log.i(TAG, String.format("teKeyState %d isdown %s", connection, d)); //printer(String.format("keystate %d isdown %s", connection, d)); } 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 static 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 static 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 static 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 static void teSpeak(int connection, byte [] msg, boolean flush, boolean synchronous) { CommunicationThread cm = clients.get(connection); if (cm != null) cm.speak(msg, flush, synchronous); } public static void teDepress(int connection, int vk_key) { // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731 Log.i(TAG, String.format("teDepress %d, %d", connection, vk_key)); CommunicationThread cm = clients.get(connection); if (cm != null) { char[] arg = new char[3]; arg[2] = (char) (vk_key & 0x0000ffff); cm.clientWrite(MONITOR_CMD_DEPRESS, arg); } } public static void teShowUrl(int connection, char [] url) { int len = url.length; CommunicationThread cm = clients.get(connection); if (cm != null) { char[] arg2 = new char[2 + len]; int base = 2; System.arraycopy(url, 0, arg2, base, len); cm.clientWrite(MONITOR_CMD_SHOWURL, arg2); } } }