Home | History | Annotate | Download | only in testing
      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.testing;
     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 import com.android.launcher3.util.Thunk;
     38 
     39 public 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