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);
+    }
+}