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