12de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhoupackage com.google.android.experimental.svcmonitor;
22de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
32de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.app.Service;
42de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.content.Intent;
52de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.os.IBinder;
62de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.os.SystemClock;
72de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.util.Log;
82de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
92de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.IOException;
102de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.InputStreamReader;
112de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.BufferedReader;
122de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.FileInputStream;
132de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.lang.Runnable;
142de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.lang.Thread;
152de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.util.Set;
162de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
172de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhoupublic class SvcMonitor extends Service {
182de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public static final String TAG = "svcmonitor";
192de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    String javaProc, halProc;
202de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    volatile Thread tMonitor;
212de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    int period;
222de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
232de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public SvcMonitor() {};
242de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
252de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    @Override
262de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public int onStartCommand(Intent intent, int flags, int startId) {
272de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (intent == null) {
282de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            stopSelf();
292de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return 0;
302de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
312de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        Log.d(TAG, "Starting SvcMonitor");
322de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if ("stop".equals(intent.getAction())) {
332de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            stopService();
342de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } else if ("start".equals(intent.getAction())) {
352de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            startMonitor(intent);
362de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } else if ("change".equals(intent.getAction())) {
372de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            changeConfig(intent);
382de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } else {
392de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "unknown action: + " + intent.getAction());
402de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
412de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        return 0;
422de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
432de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
442de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    private void changeConfig(Intent intent) {
452de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (tMonitor == null) {
462de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Service not active. Start service first");
472de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return;
482de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
492de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        stopThread();
502de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        startMonitor(intent);
512de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
522de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
532de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    private void startMonitor(Intent intent) {
542de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (tMonitor != null) {
552de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "thread already active");
562de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return;
572de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
582de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        javaProc = intent.getStringExtra("java");
592de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        halProc = intent.getStringExtra("hal");
602de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        period = intent.getIntExtra("period", 1000);
612de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (javaProc == null || halProc == null || period < 100) {
622de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Failed starting monitor, invalid arguments.");
632de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            stopSelf();
642de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return;
652de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
662de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        Runnable monitor = new MonitorRunnable(this);
672de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        tMonitor = new Thread(monitor);
682de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        tMonitor.start();
692de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
702de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
712de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    private void stopService() {
722de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        stopThread();
732de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        stopSelf();
742de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        Log.d(TAG, "SvcMonitor stopped");
752de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
762de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
772de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    private void stopThread() {
782de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (tMonitor == null) {
792de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "no active thread");
802de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return;
812de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
822de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        Log.d(TAG, "interrupting monitor thread");
832de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        tMonitor.interrupt();
842de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        try {
852de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            tMonitor.join();
862de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } catch (InterruptedException e) {
872de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Unable to finish monitor thread");
882de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
892de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        tMonitor = null;
902de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
912de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
922de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    @Override
932de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public void onDestroy() {
942de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        super.onDestroy();
952de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
962de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
972de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    @Override
982de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public IBinder onBind(Intent intent) {
992de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        throw new UnsupportedOperationException("Not yet implemented");
1002de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
1012de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1022de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public static class MonitorRunnable implements Runnable {
1032de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        long java_time_old, hal_time_old, cpu_time_old = -1;
1042de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        String javaPID, halPID;
1052de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        SvcMonitor svcmonitor;
1062de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        static String javaProcTAG;
1072de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        int period;
1082de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1092de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        public MonitorRunnable(SvcMonitor svcmonitor) {
1102de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.svcmonitor = svcmonitor;
1112de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.period = svcmonitor.period;
1122de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            javaPID = getPIDof(svcmonitor.javaProc);
1132de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            halPID = getPIDof(svcmonitor.halProc);
1142de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            java_time_old = getPsTime(javaPID);
1152de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            hal_time_old = getPsTime(halPID);
1162de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            cpu_time_old = getPsTime("");
1172de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray());
1182de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1192de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1202de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        @Override
1212de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        public void run() {
1222de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            if (halPID.isEmpty() || javaPID.isEmpty()) {
1232de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(javaProcTAG, "No such process: " +
1242de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc));
1252de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                return;
1262de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
1272de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            while (!Thread.interrupted()) {
1282de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                calculateUsage();
1292de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                SystemClock.sleep(period);
1302de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
1312de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Stopping monitor thread");
1322de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1332de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1342de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private void calculateUsage() {
1352de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            long java_time = getPsTime(javaPID);
1362de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            long hal_time = getPsTime(halPID);
1372de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            long cpu_time = getPsTime("");
1382de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1392de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            if (cpu_time_old >= 0) {
1402de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                float java_diff = (float) (java_time - java_time_old);
1412de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                float hal_diff = (float) (hal_time - hal_time_old);
1422de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                float cpu_diff = (float) (cpu_time - cpu_time_old);
1432de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.w(javaProcTAG, "\n----------------\n");
1442de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.w(javaProcTAG, "JAVA level CPU: "
1452de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        + (java_diff * 100.0 / cpu_diff) + "%\n");
1462de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.w(javaProcTAG, " HAL level CPU: "
1472de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        + (hal_diff * 100.0 / cpu_diff) + "%\n");
1482de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.w(javaProcTAG, " SYS level CPU: "
1492de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n");
1502de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } else {
1512de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.w(TAG, "Waiting for status\n");
1522de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
1532de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1542de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            java_time_old = java_time;
1552de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            hal_time_old = hal_time;
1562de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            cpu_time_old = cpu_time;
1572de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1582de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1592de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private String getPIDof(String psName) {
1602de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            String pid = "";
1612de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1622de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
1632de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName};
1642de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Process ps = Runtime.getRuntime().exec(cmd);
1652de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                BufferedReader in = new BufferedReader(
1662de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        new InputStreamReader(ps.getInputStream()));
1672de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                String temp = in.readLine();
1682de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (temp == null || temp.isEmpty())
1692de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    throw new IOException("No such process: " + psName);
1702de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                pid = temp.split(" +")[1];
1712de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                in.close();
1722de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (IOException e) {
1732de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e);
1742de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
1752de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return pid;
1762de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1772de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1782de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private long getPsTime(String pid) {
1792de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            String psStat = getPsStat("/" + pid);
1802de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            String[] statBreakDown = psStat.split(" +");
1812de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            long psTime;
1822de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1832de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            if (pid.isEmpty()) {
1842de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                psTime = Long.parseLong(statBreakDown[1])
1852de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        + Long.parseLong(statBreakDown[2])
1862de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        + Long.parseLong(statBreakDown[3])
1872de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        + Long.parseLong(statBreakDown[4]);
1882de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } else {
1892de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                psTime = Long.parseLong(statBreakDown[13])
1902de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        + Long.parseLong(statBreakDown[14]);
1912de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
1922de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1932de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return psTime;
1942de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1952de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1962de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private String getPsStat(String psname) {
1972de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            String stat = "";
1982de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
1992de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                FileInputStream fs = new FileInputStream("/proc" + psname + "/stat");
2002de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                BufferedReader br = new BufferedReader(new InputStreamReader(fs));
2012de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                stat = br.readLine();
2022de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                fs.close();
2032de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (IOException e) {
2042de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "Error retreiving stat. \n");
2052de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
2062de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return stat;
2072de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
2082de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
2092de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou}
210