1package com.google.android.experimental.svcmonitor;
2
3import android.app.Service;
4import android.content.Intent;
5import android.os.IBinder;
6import android.os.SystemClock;
7import android.util.Log;
8
9import java.io.IOException;
10import java.io.InputStreamReader;
11import java.io.BufferedReader;
12import java.io.FileInputStream;
13import java.lang.Runnable;
14import java.lang.Thread;
15import java.util.Set;
16
17public class SvcMonitor extends Service {
18    public static final String TAG = "svcmonitor";
19    String javaProc, halProc;
20    volatile Thread tMonitor;
21    int period;
22
23    public SvcMonitor() {};
24
25    @Override
26    public int onStartCommand(Intent intent, int flags, int startId) {
27        if (intent == null) {
28            stopSelf();
29            return 0;
30        }
31        Log.d(TAG, "Starting SvcMonitor");
32        if ("stop".equals(intent.getAction())) {
33            stopService();
34        } else if ("start".equals(intent.getAction())) {
35            startMonitor(intent);
36        } else if ("change".equals(intent.getAction())) {
37            changeConfig(intent);
38        } else {
39            Log.d(TAG, "unknown action: + " + intent.getAction());
40        }
41        return 0;
42    }
43
44    private void changeConfig(Intent intent) {
45        if (tMonitor == null) {
46            Log.d(TAG, "Service not active. Start service first");
47            return;
48        }
49        stopThread();
50        startMonitor(intent);
51    }
52
53    private void startMonitor(Intent intent) {
54        if (tMonitor != null) {
55            Log.d(TAG, "thread already active");
56            return;
57        }
58        javaProc = intent.getStringExtra("java");
59        halProc = intent.getStringExtra("hal");
60        period = intent.getIntExtra("period", 1000);
61        if (javaProc == null || halProc == null || period < 100) {
62            Log.d(TAG, "Failed starting monitor, invalid arguments.");
63            stopSelf();
64            return;
65        }
66        Runnable monitor = new MonitorRunnable(this);
67        tMonitor = new Thread(monitor);
68        tMonitor.start();
69    }
70
71    private void stopService() {
72        stopThread();
73        stopSelf();
74        Log.d(TAG, "SvcMonitor stopped");
75    }
76
77    private void stopThread() {
78        if (tMonitor == null) {
79            Log.d(TAG, "no active thread");
80            return;
81        }
82        Log.d(TAG, "interrupting monitor thread");
83        tMonitor.interrupt();
84        try {
85            tMonitor.join();
86        } catch (InterruptedException e) {
87            Log.d(TAG, "Unable to finish monitor thread");
88        }
89        tMonitor = null;
90    }
91
92    @Override
93    public void onDestroy() {
94        super.onDestroy();
95    }
96
97    @Override
98    public IBinder onBind(Intent intent) {
99        throw new UnsupportedOperationException("Not yet implemented");
100    }
101
102    public static class MonitorRunnable implements Runnable {
103        long java_time_old, hal_time_old, cpu_time_old = -1;
104        String javaPID, halPID;
105        SvcMonitor svcmonitor;
106        static String javaProcTAG;
107        int period;
108
109        public MonitorRunnable(SvcMonitor svcmonitor) {
110            this.svcmonitor = svcmonitor;
111            this.period = svcmonitor.period;
112            javaPID = getPIDof(svcmonitor.javaProc);
113            halPID = getPIDof(svcmonitor.halProc);
114            java_time_old = getPsTime(javaPID);
115            hal_time_old = getPsTime(halPID);
116            cpu_time_old = getPsTime("");
117            javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray());
118        }
119
120        @Override
121        public void run() {
122            if (halPID.isEmpty() || javaPID.isEmpty()) {
123                Log.d(javaProcTAG, "No such process: " +
124                        (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc));
125                return;
126            }
127            while (!Thread.interrupted()) {
128                calculateUsage();
129                SystemClock.sleep(period);
130            }
131            Log.d(TAG, "Stopping monitor thread");
132        }
133
134        private void calculateUsage() {
135            long java_time = getPsTime(javaPID);
136            long hal_time = getPsTime(halPID);
137            long cpu_time = getPsTime("");
138
139            if (cpu_time_old >= 0) {
140                float java_diff = (float) (java_time - java_time_old);
141                float hal_diff = (float) (hal_time - hal_time_old);
142                float cpu_diff = (float) (cpu_time - cpu_time_old);
143                Log.w(javaProcTAG, "\n----------------\n");
144                Log.w(javaProcTAG, "JAVA level CPU: "
145                        + (java_diff * 100.0 / cpu_diff) + "%\n");
146                Log.w(javaProcTAG, " HAL level CPU: "
147                        + (hal_diff * 100.0 / cpu_diff) + "%\n");
148                Log.w(javaProcTAG, " SYS level CPU: "
149                        + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n");
150            } else {
151                Log.w(TAG, "Waiting for status\n");
152            }
153
154            java_time_old = java_time;
155            hal_time_old = hal_time;
156            cpu_time_old = cpu_time;
157        }
158
159        private String getPIDof(String psName) {
160            String pid = "";
161
162            try {
163                String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName};
164                Process ps = Runtime.getRuntime().exec(cmd);
165                BufferedReader in = new BufferedReader(
166                        new InputStreamReader(ps.getInputStream()));
167                String temp = in.readLine();
168                if (temp == null || temp.isEmpty())
169                    throw new IOException("No such process: " + psName);
170                pid = temp.split(" +")[1];
171                in.close();
172            } catch (IOException e) {
173                Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e);
174            }
175            return pid;
176        }
177
178        private long getPsTime(String pid) {
179            String psStat = getPsStat("/" + pid);
180            String[] statBreakDown = psStat.split(" +");
181            long psTime;
182
183            if (pid.isEmpty()) {
184                psTime = Long.parseLong(statBreakDown[1])
185                        + Long.parseLong(statBreakDown[2])
186                        + Long.parseLong(statBreakDown[3])
187                        + Long.parseLong(statBreakDown[4]);
188            } else {
189                psTime = Long.parseLong(statBreakDown[13])
190                        + Long.parseLong(statBreakDown[14]);
191            }
192
193            return psTime;
194        }
195
196        private String getPsStat(String psname) {
197            String stat = "";
198            try {
199                FileInputStream fs = new FileInputStream("/proc" + psname + "/stat");
200                BufferedReader br = new BufferedReader(new InputStreamReader(fs));
201                stat = br.readLine();
202                fs.close();
203            } catch (IOException e) {
204                Log.d(TAG, "Error retreiving stat. \n");
205            }
206            return stat;
207        }
208    }
209}
210