1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui;
18 
19 import android.app.Service;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.graphics.Canvas;
23 import android.graphics.Paint;
24 import android.graphics.PixelFormat;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.Message;
28 import android.view.Gravity;
29 import android.view.View;
30 import android.view.WindowManager;
31 
32 import com.android.internal.os.ProcessCpuTracker;
33 
34 public class LoadAverageService extends Service {
35     private View mView;
36 
37     private static final class CpuTracker extends ProcessCpuTracker {
38         String mLoadText;
39         int mLoadWidth;
40 
41         private final Paint mPaint;
42 
CpuTracker(Paint paint)43         CpuTracker(Paint paint) {
44             super(false);
45             mPaint = paint;
46         }
47 
48         @Override
onLoadChanged(float load1, float load5, float load15)49         public void onLoadChanged(float load1, float load5, float load15) {
50             mLoadText = load1 + " / " + load5 + " / " + load15;
51             mLoadWidth = (int)mPaint.measureText(mLoadText);
52         }
53 
54         @Override
onMeasureProcessName(String name)55         public int onMeasureProcessName(String name) {
56             return (int)mPaint.measureText(name);
57         }
58     }
59 
60     private class LoadView extends View {
61         private Handler mHandler = new Handler() {
62             @Override
63             public void handleMessage(Message msg) {
64                 if (msg.what == 1) {
65                     mStats.update();
66                     updateDisplay();
67                     Message m = obtainMessage(1);
68                     sendMessageDelayed(m, 2000);
69                 }
70             }
71         };
72 
73         private final CpuTracker mStats;
74 
75         private Paint mLoadPaint;
76         private Paint mAddedPaint;
77         private Paint mRemovedPaint;
78         private Paint mShadowPaint;
79         private Paint mShadow2Paint;
80         private Paint mIrqPaint;
81         private Paint mSystemPaint;
82         private Paint mUserPaint;
83         private float mAscent;
84         private int mFH;
85 
86         private int mNeededWidth;
87         private int mNeededHeight;
88 
LoadView(Context c)89         LoadView(Context c) {
90             super(c);
91 
92             setPadding(4, 4, 4, 4);
93             //setBackgroundResource(com.android.internal.R.drawable.load_average_background);
94 
95             // Need to scale text size by density...  but we won't do it
96             // linearly, because with higher dps it is nice to squeeze the
97             // text a bit to fit more of it.  And with lower dps, trying to
98             // go much smaller will result in unreadable text.
99             int textSize = 10;
100             float density = c.getResources().getDisplayMetrics().density;
101             if (density < 1) {
102                 textSize = 9;
103             } else {
104                 textSize = (int)(10*density);
105                 if (textSize < 10) {
106                     textSize = 10;
107                 }
108             }
109             mLoadPaint = new Paint();
110             mLoadPaint.setAntiAlias(true);
111             mLoadPaint.setTextSize(textSize);
112             mLoadPaint.setARGB(255, 255, 255, 255);
113 
114             mAddedPaint = new Paint();
115             mAddedPaint.setAntiAlias(true);
116             mAddedPaint.setTextSize(textSize);
117             mAddedPaint.setARGB(255, 128, 255, 128);
118 
119             mRemovedPaint = new Paint();
120             mRemovedPaint.setAntiAlias(true);
121             mRemovedPaint.setStrikeThruText(true);
122             mRemovedPaint.setTextSize(textSize);
123             mRemovedPaint.setARGB(255, 255, 128, 128);
124 
125             mShadowPaint = new Paint();
126             mShadowPaint.setAntiAlias(true);
127             mShadowPaint.setTextSize(textSize);
128             //mShadowPaint.setFakeBoldText(true);
129             mShadowPaint.setARGB(192, 0, 0, 0);
130             mLoadPaint.setShadowLayer(4, 0, 0, 0xff000000);
131 
132             mShadow2Paint = new Paint();
133             mShadow2Paint.setAntiAlias(true);
134             mShadow2Paint.setTextSize(textSize);
135             //mShadow2Paint.setFakeBoldText(true);
136             mShadow2Paint.setARGB(192, 0, 0, 0);
137             mLoadPaint.setShadowLayer(2, 0, 0, 0xff000000);
138 
139             mIrqPaint = new Paint();
140             mIrqPaint.setARGB(0x80, 0, 0, 0xff);
141             mIrqPaint.setShadowLayer(2, 0, 0, 0xff000000);
142             mSystemPaint = new Paint();
143             mSystemPaint.setARGB(0x80, 0xff, 0, 0);
144             mSystemPaint.setShadowLayer(2, 0, 0, 0xff000000);
145             mUserPaint = new Paint();
146             mUserPaint.setARGB(0x80, 0, 0xff, 0);
147             mSystemPaint.setShadowLayer(2, 0, 0, 0xff000000);
148 
149             mAscent = mLoadPaint.ascent();
150             float descent = mLoadPaint.descent();
151             mFH = (int)(descent - mAscent + .5f);
152 
153             mStats = new CpuTracker(mLoadPaint);
154             mStats.init();
155             updateDisplay();
156         }
157 
158         @Override
onAttachedToWindow()159         protected void onAttachedToWindow() {
160             super.onAttachedToWindow();
161             mHandler.sendEmptyMessage(1);
162         }
163 
164         @Override
onDetachedFromWindow()165         protected void onDetachedFromWindow() {
166             super.onDetachedFromWindow();
167             mHandler.removeMessages(1);
168         }
169 
170         @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)171         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
172             setMeasuredDimension(resolveSize(mNeededWidth, widthMeasureSpec),
173                     resolveSize(mNeededHeight, heightMeasureSpec));
174         }
175 
176         @Override
onDraw(Canvas canvas)177         public void onDraw(Canvas canvas) {
178             super.onDraw(canvas);
179             final int W = mNeededWidth;
180             final int RIGHT = getWidth()-1;
181 
182             final CpuTracker stats = mStats;
183             final int userTime = stats.getLastUserTime();
184             final int systemTime = stats.getLastSystemTime();
185             final int iowaitTime = stats.getLastIoWaitTime();
186             final int irqTime = stats.getLastIrqTime();
187             final int softIrqTime = stats.getLastSoftIrqTime();
188             final int idleTime = stats.getLastIdleTime();
189 
190             final int totalTime = userTime+systemTime+iowaitTime+irqTime+softIrqTime+idleTime;
191             if (totalTime == 0) {
192                 return;
193             }
194             int userW = (userTime*W)/totalTime;
195             int systemW = (systemTime*W)/totalTime;
196             int irqW = ((iowaitTime+irqTime+softIrqTime)*W)/totalTime;
197 
198             int paddingRight = getPaddingRight();
199             int x = RIGHT - paddingRight;
200             int top = getPaddingTop() + 2;
201             int bottom = getPaddingTop() + mFH - 2;
202 
203             if (irqW > 0) {
204                 canvas.drawRect(x-irqW, top, x, bottom, mIrqPaint);
205                 x -= irqW;
206             }
207             if (systemW > 0) {
208                 canvas.drawRect(x-systemW, top, x, bottom, mSystemPaint);
209                 x -= systemW;
210             }
211             if (userW > 0) {
212                 canvas.drawRect(x-userW, top, x, bottom, mUserPaint);
213                 x -= userW;
214             }
215 
216             int y = getPaddingTop() - (int)mAscent;
217             canvas.drawText(stats.mLoadText, RIGHT-paddingRight-stats.mLoadWidth-1,
218                     y-1, mShadowPaint);
219             canvas.drawText(stats.mLoadText, RIGHT-paddingRight-stats.mLoadWidth-1,
220                     y+1, mShadowPaint);
221             canvas.drawText(stats.mLoadText, RIGHT-paddingRight-stats.mLoadWidth+1,
222                     y-1, mShadow2Paint);
223             canvas.drawText(stats.mLoadText, RIGHT-paddingRight-stats.mLoadWidth+1,
224                     y+1, mShadow2Paint);
225             canvas.drawText(stats.mLoadText, RIGHT-paddingRight-stats.mLoadWidth,
226                     y, mLoadPaint);
227 
228             int N = stats.countWorkingStats();
229             for (int i=0; i<N; i++) {
230                 CpuTracker.Stats st = stats.getWorkingStats(i);
231                 y += mFH;
232                 top += mFH;
233                 bottom += mFH;
234 
235                 userW = (st.rel_utime*W)/totalTime;
236                 systemW = (st.rel_stime*W)/totalTime;
237                 x = RIGHT - paddingRight;
238                 if (systemW > 0) {
239                     canvas.drawRect(x-systemW, top, x, bottom, mSystemPaint);
240                     x -= systemW;
241                 }
242                 if (userW > 0) {
243                     canvas.drawRect(x-userW, top, x, bottom, mUserPaint);
244                     x -= userW;
245                 }
246 
247                 canvas.drawText(st.name, RIGHT-paddingRight-st.nameWidth-1,
248                         y-1, mShadowPaint);
249                 canvas.drawText(st.name, RIGHT-paddingRight-st.nameWidth-1,
250                         y+1, mShadowPaint);
251                 canvas.drawText(st.name, RIGHT-paddingRight-st.nameWidth+1,
252                         y-1, mShadow2Paint);
253                 canvas.drawText(st.name, RIGHT-paddingRight-st.nameWidth+1,
254                         y+1, mShadow2Paint);
255                 Paint p = mLoadPaint;
256                 if (st.added) p = mAddedPaint;
257                 if (st.removed) p = mRemovedPaint;
258                 canvas.drawText(st.name, RIGHT-paddingRight-st.nameWidth, y, p);
259             }
260         }
261 
updateDisplay()262         void updateDisplay() {
263             final CpuTracker stats = mStats;
264             final int NW = stats.countWorkingStats();
265 
266             int maxWidth = stats.mLoadWidth;
267             for (int i=0; i<NW; i++) {
268                 CpuTracker.Stats st = stats.getWorkingStats(i);
269                 if (st.nameWidth > maxWidth) {
270                     maxWidth = st.nameWidth;
271                 }
272             }
273 
274             int neededWidth = getPaddingLeft() + getPaddingRight() + maxWidth;
275             int neededHeight = getPaddingTop() + getPaddingBottom() + (mFH*(1+NW));
276             if (neededWidth != mNeededWidth || neededHeight != mNeededHeight) {
277                 mNeededWidth = neededWidth;
278                 mNeededHeight = neededHeight;
279                 requestLayout();
280             } else {
281                 invalidate();
282             }
283         }
284     }
285 
286     @Override
onCreate()287     public void onCreate() {
288         super.onCreate();
289         mView = new LoadView(this);
290         WindowManager.LayoutParams params = new WindowManager.LayoutParams(
291             WindowManager.LayoutParams.MATCH_PARENT,
292             WindowManager.LayoutParams.WRAP_CONTENT,
293             WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
294             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
295             WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
296             PixelFormat.TRANSLUCENT);
297         params.gravity = Gravity.END | Gravity.TOP;
298         params.setTitle("Load Average");
299         WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
300         wm.addView(mView, params);
301     }
302 
303     @Override
onDestroy()304     public void onDestroy() {
305         super.onDestroy();
306         ((WindowManager)getSystemService(WINDOW_SERVICE)).removeView(mView);
307         mView = null;
308     }
309 
310     @Override
onBind(Intent intent)311     public IBinder onBind(Intent intent) {
312         return null;
313     }
314 
315 }
316