1 /* 2 * Copyright (C) 2013 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.launcher3; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.graphics.Canvas; 24 import android.graphics.Color; 25 import android.graphics.Paint; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Message; 29 import android.util.AttributeSet; 30 import android.util.Log; 31 import android.util.TypedValue; 32 import android.view.Gravity; 33 import android.view.View; 34 import android.widget.LinearLayout; 35 import android.widget.TextView; 36 37 public class WeightWatcher extends LinearLayout { 38 private static final int RAM_GRAPH_RSS_COLOR = 0xFF990000; 39 private static final int RAM_GRAPH_PSS_COLOR = 0xFF99CC00; 40 private static final int TEXT_COLOR = 0xFFFFFFFF; 41 private static final int BACKGROUND_COLOR = 0xc0000000; 42 43 private static final int UPDATE_RATE = 5000; 44 45 private static final int MSG_START = 1; 46 private static final int MSG_STOP = 2; 47 private static final int MSG_UPDATE = 3; 48 49 static int indexOf(int[] a, int x) { 50 for (int i=0; i<a.length; i++) { 51 if (a[i] == x) return i; 52 } 53 return -1; 54 } 55 56 Handler mHandler = new Handler() { 57 @Override 58 public void handleMessage(Message m) { 59 switch (m.what) { 60 case MSG_START: 61 mHandler.sendEmptyMessage(MSG_UPDATE); 62 break; 63 case MSG_STOP: 64 mHandler.removeMessages(MSG_UPDATE); 65 break; 66 case MSG_UPDATE: 67 int[] pids = mMemoryService.getTrackedProcesses(); 68 69 final int N = getChildCount(); 70 if (pids.length != N) initViews(); 71 else for (int i=0; i<N; i++) { 72 ProcessWatcher pw = ((ProcessWatcher) getChildAt(i)); 73 if (indexOf(pids, pw.getPid()) < 0) { 74 initViews(); 75 break; 76 } 77 pw.update(); 78 } 79 mHandler.sendEmptyMessageDelayed(MSG_UPDATE, UPDATE_RATE); 80 break; 81 } 82 } 83 }; 84 private MemoryTracker mMemoryService; 85 86 public WeightWatcher(Context context, AttributeSet attrs) { 87 super(context, attrs); 88 89 ServiceConnection connection = new ServiceConnection() { 90 public void onServiceConnected(ComponentName className, IBinder service) { 91 mMemoryService = ((MemoryTracker.MemoryTrackerInterface)service).getService(); 92 initViews(); 93 } 94 95 public void onServiceDisconnected(ComponentName className) { 96 mMemoryService = null; 97 } 98 }; 99 context.bindService(new Intent(context, MemoryTracker.class), 100 connection, Context.BIND_AUTO_CREATE); 101 102 setOrientation(LinearLayout.VERTICAL); 103 104 setBackgroundColor(BACKGROUND_COLOR); 105 } 106 107 public void initViews() { 108 removeAllViews(); 109 int[] processes = mMemoryService.getTrackedProcesses(); 110 for (int i=0; i<processes.length; i++) { 111 final ProcessWatcher v = new ProcessWatcher(getContext()); 112 v.setPid(processes[i]); 113 addView(v); 114 } 115 } 116 117 public WeightWatcher(Context context) { 118 this(context, null); 119 } 120 121 @Override 122 public void onAttachedToWindow() { 123 super.onAttachedToWindow(); 124 mHandler.sendEmptyMessage(MSG_START); 125 } 126 127 @Override 128 public void onDetachedFromWindow() { 129 super.onDetachedFromWindow(); 130 mHandler.sendEmptyMessage(MSG_STOP); 131 } 132 133 public class ProcessWatcher extends LinearLayout { 134 GraphView mRamGraph; 135 TextView mText; 136 int mPid; 137 private MemoryTracker.ProcessMemInfo mMemInfo; 138 139 public ProcessWatcher(Context context) { 140 this(context, null); 141 } 142 143 public ProcessWatcher(Context context, AttributeSet attrs) { 144 super(context, attrs); 145 146 final float dp = getResources().getDisplayMetrics().density; 147 148 mText = new TextView(getContext()); 149 mText.setTextColor(TEXT_COLOR); 150 mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, 10 * dp); 151 mText.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); 152 153 final int p = (int)(2*dp); 154 setPadding(p, 0, p, 0); 155 156 mRamGraph = new GraphView(getContext()); 157 158 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 159 0, 160 (int)(14 * dp), 161 1f 162 ); 163 164 addView(mText, params); 165 params.leftMargin = (int)(4*dp); 166 params.weight = 0f; 167 params.width = (int)(200 * dp); 168 addView(mRamGraph, params); 169 } 170 171 public void setPid(int pid) { 172 mPid = pid; 173 mMemInfo = mMemoryService.getMemInfo(mPid); 174 if (mMemInfo == null) { 175 Log.v("WeightWatcher", "Missing info for pid " + mPid + ", removing view: " + this); 176 initViews(); 177 } 178 } 179 180 public int getPid() { 181 return mPid; 182 } 183 184 public String getUptimeString() { 185 long sec = mMemInfo.getUptime() / 1000; 186 StringBuilder sb = new StringBuilder(); 187 long days = sec / 86400; 188 if (days > 0) { 189 sec -= days * 86400; 190 sb.append(days); 191 sb.append("d"); 192 } 193 194 long hours = sec / 3600; 195 if (hours > 0) { 196 sec -= hours * 3600; 197 sb.append(hours); 198 sb.append("h"); 199 } 200 201 long mins = sec / 60; 202 if (mins > 0) { 203 sec -= mins * 60; 204 sb.append(mins); 205 sb.append("m"); 206 } 207 208 sb.append(sec); 209 sb.append("s"); 210 return sb.toString(); 211 } 212 213 public void update() { 214 //Log.v("WeightWatcher.ProcessWatcher", 215 // "MSG_UPDATE pss=" + mMemInfo.currentPss); 216 mText.setText("(" + mPid 217 + (mPid == android.os.Process.myPid() 218 ? "/A" // app 219 : "/S") // service 220 + ") up " + getUptimeString() 221 + " P=" + mMemInfo.currentPss 222 + " U=" + mMemInfo.currentUss 223 ); 224 mRamGraph.invalidate(); 225 } 226 227 public class GraphView extends View { 228 Paint pssPaint, ussPaint, headPaint; 229 230 public GraphView(Context context, AttributeSet attrs) { 231 super(context, attrs); 232 233 pssPaint = new Paint(); 234 pssPaint.setColor(RAM_GRAPH_PSS_COLOR); 235 ussPaint = new Paint(); 236 ussPaint.setColor(RAM_GRAPH_RSS_COLOR); 237 headPaint = new Paint(); 238 headPaint.setColor(Color.WHITE); 239 } 240 241 public GraphView(Context context) { 242 this(context, null); 243 } 244 245 @Override 246 public void onDraw(Canvas c) { 247 int w = c.getWidth(); 248 int h = c.getHeight(); 249 250 if (mMemInfo == null) return; 251 252 final int N = mMemInfo.pss.length; 253 final float barStep = (float) w / N; 254 final float barWidth = Math.max(1, barStep); 255 final float scale = (float) h / mMemInfo.max; 256 257 int i; 258 float x; 259 for (i=0; i<N; i++) { 260 x = i * barStep; 261 c.drawRect(x, h - scale * mMemInfo.pss[i], x + barWidth, h, pssPaint); 262 c.drawRect(x, h - scale * mMemInfo.uss[i], x + barWidth, h, ussPaint); 263 } 264 x = mMemInfo.head * barStep; 265 c.drawRect(x, 0, x + barWidth, h, headPaint); 266 } 267 } 268 } 269 } 270