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