Proc stats UI improvements.

Now have a chart showing the memory state, and text showing the
current memory state.  Trying to do better at picking the application
to blame for a process hosting multiple apps.  Start of infrastructure
for more detailed reporting.

Change-Id: I93ca7ecf2fd0bc01e3be8d28b80212ac78fe7607
This commit is contained in:
Dianne Hackborn
2013-08-09 16:17:27 -07:00
parent 53083ab2bb
commit a582a05195
7 changed files with 263 additions and 31 deletions

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.settings.applications.LinearColorBar
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:id="@+android:id/linear_color_bar"
android:paddingEnd="?android:attr/scrollbarSize"
android:textAppearance="?android:attr/textAppearanceMedium"
android:shadowRadius="4"
android:shadowColor="?android:attr/colorBackground"
android:shadowDx="2"
android:shadowDy="2">
</com.android.settings.applications.LinearColorBar>

View File

@@ -1149,4 +1149,16 @@
<item>Always allow</item> <item>Always allow</item>
</string-array> </string-array>
<!-- [CHAR LIMIT=30] Labels for memory states -->
<string-array name="ram_states">
<!-- Normal desired memory state. -->
<item>normal</item>
<!-- Moderate memory state, not as good as normal. -->
<item>moderate</item>
<!-- Memory is running low. -->
<item>low</item>
<!-- Memory is critical. -->
<item>critical</item>
</string-array>
</resources> </resources>

View File

@@ -3557,7 +3557,10 @@
<!-- [CHAR LIMIT=NONE] Label for amount of memory use --> <!-- [CHAR LIMIT=NONE] Label for amount of memory use -->
<string name="app_memory_use">Memory use</string> <string name="app_memory_use">Memory use</string>
<!-- [CHAR LIMIT=NONE] Label for process stats, duration of time the stats are over --> <!-- [CHAR LIMIT=NONE] Label for process stats, duration of time the stats are over -->
<string name="process_stats_total_duration">Over <xliff:g id="time">%1$s</xliff:g></string> <string name="process_stats_total_duration">Stats over <xliff:g id="time">%1$s</xliff:g></string>
<!-- [CHAR LIMIT=NONE] Label for process stats, duration of time the stats are over -->
<string name="process_stats_memory_status">Device memory is currently
<xliff:g id="memstate">%1$s</xliff:g></string>
<!-- Voice input/output settings --><skip /> <!-- Voice input/output settings --><skip />
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to speech functionality --> <!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to speech functionality -->

View File

@@ -23,6 +23,11 @@ public class LinearColorBar extends LinearLayout {
private float mYellowRatio; private float mYellowRatio;
private float mGreenRatio; private float mGreenRatio;
private int mLeftColor = LEFT_COLOR;
private int mMiddleColor = MIDDLE_COLOR;
private int mRightColor = RIGHT_COLOR;
private boolean mShowIndicator = true;
private boolean mShowingGreen; private boolean mShowingGreen;
final Rect mRect = new Rect(); final Rect mRect = new Rect();
@@ -57,6 +62,20 @@ public class LinearColorBar extends LinearLayout {
invalidate(); invalidate();
} }
public void setColors(int red, int yellow, int green) {
mLeftColor = red;
mMiddleColor = yellow;
mRightColor = green;
updateIndicator();
invalidate();
}
public void setShowIndicator(boolean showIndicator) {
mShowIndicator = showIndicator;
updateIndicator();
invalidate();
}
public void setShowingGreen(boolean showingGreen) { public void setShowingGreen(boolean showingGreen) {
if (mShowingGreen != showingGreen) { if (mShowingGreen != showingGreen) {
mShowingGreen = showingGreen; mShowingGreen = showingGreen;
@@ -70,12 +89,15 @@ public class LinearColorBar extends LinearLayout {
if (off < 0) off = 0; if (off < 0) off = 0;
mRect.top = off; mRect.top = off;
mRect.bottom = getHeight(); mRect.bottom = getHeight();
if (!mShowIndicator) {
return;
}
if (mShowingGreen) { if (mShowingGreen) {
mColorGradientPaint.setShader(new LinearGradient( mColorGradientPaint.setShader(new LinearGradient(
0, 0, 0, off-2, RIGHT_COLOR&0xffffff, RIGHT_COLOR, Shader.TileMode.CLAMP)); 0, 0, 0, off-2, mRightColor &0xffffff, mRightColor, Shader.TileMode.CLAMP));
} else { } else {
mColorGradientPaint.setShader(new LinearGradient( mColorGradientPaint.setShader(new LinearGradient(
0, 0, 0, off-2, MIDDLE_COLOR&0xffffff, MIDDLE_COLOR, Shader.TileMode.CLAMP)); 0, 0, 0, off-2, mMiddleColor&0xffffff, mMiddleColor, Shader.TileMode.CLAMP));
} }
mEdgeGradientPaint.setShader(new LinearGradient( mEdgeGradientPaint.setShader(new LinearGradient(
0, 0, 0, off/2, 0x00a0a0a0, 0xffa0a0a0, Shader.TileMode.CLAMP)); 0, 0, 0, off/2, 0x00a0a0a0, 0xffa0a0a0, Shader.TileMode.CLAMP));
@@ -111,7 +133,7 @@ public class LinearColorBar extends LinearLayout {
if (mLastInterestingLeft != indicatorLeft || mLastInterestingRight != indicatorRight) { if (mLastInterestingLeft != indicatorLeft || mLastInterestingRight != indicatorRight) {
mColorPath.reset(); mColorPath.reset();
mEdgePath.reset(); mEdgePath.reset();
if (indicatorLeft < indicatorRight) { if (mShowIndicator && indicatorLeft < indicatorRight) {
final int midTopY = mRect.top; final int midTopY = mRect.top;
final int midBottomY = 0; final int midBottomY = 0;
final int xoff = 2; final int xoff = 2;
@@ -146,7 +168,7 @@ public class LinearColorBar extends LinearLayout {
if (left < right) { if (left < right) {
mRect.left = left; mRect.left = left;
mRect.right = right; mRect.right = right;
mPaint.setColor(LEFT_COLOR); mPaint.setColor(mLeftColor);
canvas.drawRect(mRect, mPaint); canvas.drawRect(mRect, mPaint);
width -= (right-left); width -= (right-left);
left = right; left = right;
@@ -157,7 +179,7 @@ public class LinearColorBar extends LinearLayout {
if (left < right) { if (left < right) {
mRect.left = left; mRect.left = left;
mRect.right = right; mRect.right = right;
mPaint.setColor(MIDDLE_COLOR); mPaint.setColor(mMiddleColor);
canvas.drawRect(mRect, mPaint); canvas.drawRect(mRect, mPaint);
width -= (right-left); width -= (right-left);
left = right; left = right;
@@ -168,7 +190,7 @@ public class LinearColorBar extends LinearLayout {
if (left < right) { if (left < right) {
mRect.left = left; mRect.left = left;
mRect.right = right; mRect.right = right;
mPaint.setColor(RIGHT_COLOR); mPaint.setColor(mRightColor);
canvas.drawRect(mRect, mPaint); canvas.drawRect(mRect, mPaint);
} }
} }

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import android.content.Context;
import android.preference.Preference;
import android.view.View;
import com.android.settings.R;
public class LinearColorPreference extends Preference {
float mRedRatio;
float mYellowRatio;
float mGreenRatio;
public LinearColorPreference(Context context) {
super(context);
setLayoutResource(R.layout.preference_linearcolor);
}
public void setRatios(float red, float yellow, float green) {
mRedRatio = red;
mYellowRatio = yellow;
mGreenRatio = green;
notifyChanged();
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
LinearColorBar colors = (LinearColorBar)view.findViewById(
R.id.linear_color_bar);
colors.setShowIndicator(false);
colors.setColors(0xffcc3000, 0xffcccc00, 0xff00cc30);
colors.setRatios(mRedRatio, mYellowRatio, mGreenRatio);
}
}

View File

@@ -18,7 +18,9 @@ package com.android.settings.applications;
import com.android.internal.app.ProcessStats; import com.android.internal.app.ProcessStats;
public class ProcStatsEntry { import java.util.ArrayList;
public final class ProcStatsEntry {
final String mPackage; final String mPackage;
final int mUid; final int mUid;
final String mName; final String mName;
@@ -27,7 +29,10 @@ public class ProcStatsEntry {
final long mAvgPss; final long mAvgPss;
final long mWeight; final long mWeight;
ProcStatsEntry(ProcessStats.ProcessState proc, ProcessStats.ProcessDataCollection tmpTotals) { ArrayList<Service> mServices;
public ProcStatsEntry(ProcessStats.ProcessState proc,
ProcessStats.ProcessDataCollection tmpTotals) {
ProcessStats.computeProcessData(proc, tmpTotals, 0); ProcessStats.computeProcessData(proc, tmpTotals, 0);
mPackage = proc.mPackage; mPackage = proc.mPackage;
mUid = proc.mUid; mUid = proc.mUid;
@@ -37,4 +42,35 @@ public class ProcStatsEntry {
mAvgPss = tmpTotals.avgPss; mAvgPss = tmpTotals.avgPss;
mWeight = mDuration * mAvgPss; mWeight = mDuration * mAvgPss;
} }
public void addServices(ProcessStats.PackageState pkgState) {
for (int isvc=0, NSVC=pkgState.mServices.size(); isvc<NSVC; isvc++) {
ProcessStats.ServiceState svc = pkgState.mServices.valueAt(isvc);
// XXX can't tell what process it is in!
if (mServices == null) {
mServices = new ArrayList<Service>();
}
mServices.add(new Service(svc));
}
}
public static final class Service {
final String mPackage;
final String mName;
final long mDuration;
public Service(ProcessStats.ServiceState service) {
mPackage = service.mPackage;
mName = service.mName;
mDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.ServiceState.SERVICE_STARTED,
ProcessStats.STATE_NOTHING, 0, 0)
+ ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.ServiceState.SERVICE_BOUND,
ProcessStats.STATE_NOTHING, 0, 0)
+ ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.ServiceState.SERVICE_EXEC,
ProcessStats.STATE_NOTHING, 0, 0);
}
}
} }

View File

@@ -33,6 +33,7 @@ import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -60,11 +61,24 @@ public class ProcessStatsUi extends PreferenceFragment {
static final int MAX_ITEMS_TO_LIST = 20; static final int MAX_ITEMS_TO_LIST = 20;
final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
@Override
public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
if (lhs.mWeight < rhs.mWeight) {
return 1;
} else if (lhs.mWeight > rhs.mWeight) {
return -1;
}
return 0;
}
};
private static ProcessStats sStatsXfer; private static ProcessStats sStatsXfer;
IProcessStats mProcessStats; IProcessStats mProcessStats;
UserManager mUm; UserManager mUm;
ProcessStats mStats; ProcessStats mStats;
int mMemState;
private PreferenceGroup mAppListGroup; private PreferenceGroup mAppListGroup;
private Preference mMemStatusPref; private Preference mMemStatusPref;
@@ -169,8 +183,17 @@ public class ProcessStatsUi extends PreferenceFragment {
mAppListGroup.addPreference(mMemStatusPref); mAppListGroup.addPreference(mMemStatusPref);
String durationString = Utils.formatElapsedTime(getActivity(), String durationString = Utils.formatElapsedTime(getActivity(),
mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime); mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime);
CharSequence memString;
CharSequence[] memStates = getResources().getTextArray(R.array.ram_states);
if (mMemState >= 0 && mMemState < memStates.length) {
memString = memStates[mMemState];
} else {
memString = "?";
}
mMemStatusPref.setTitle(getActivity().getString(R.string.process_stats_total_duration, mMemStatusPref.setTitle(getActivity().getString(R.string.process_stats_total_duration,
durationString)); durationString));
mMemStatusPref.setSummary(getActivity().getString(R.string.process_stats_memory_status,
memString));
/* /*
mMemStatusPref.setTitle(DateFormat.format(DateFormat.getBestDateTimePattern( mMemStatusPref.setTitle(DateFormat.format(DateFormat.getBestDateTimePattern(
getActivity().getResources().getConfiguration().locale, getActivity().getResources().getConfiguration().locale,
@@ -188,30 +211,47 @@ public class ProcessStatsUi extends PreferenceFragment {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
final PackageManager pm = getActivity().getPackageManager();
mTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations, mTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations,
mStats.mMemFactor, mStats.mStartTime, now); mStats.mMemFactor, mStats.mStartTime, now);
LinearColorPreference colors = new LinearColorPreference(getActivity());
colors.setOrder(-1);
mAppListGroup.addPreference(colors);
long[] memTimes = new long[ProcessStats.ADJ_MEM_FACTOR_COUNT];
for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
int state = imem+iscreen;
memTimes[imem] += mStats.mMemFactorDurations[state];
}
}
colors.setRatios(memTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL] / (float)mTotalTime,
(memTimes[ProcessStats.ADJ_MEM_FACTOR_LOW]
+ memTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]) / (float)mTotalTime,
memTimes[ProcessStats.ADJ_MEM_FACTOR_NORMAL] / (float)mTotalTime);
ArrayList<ProcStatsEntry> procs = new ArrayList<ProcStatsEntry>();
/*
ArrayList<ProcessStats.ProcessState> rawProcs = mStats.collectProcessesLocked( ArrayList<ProcessStats.ProcessState> rawProcs = mStats.collectProcessesLocked(
ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ, ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ,
ProcessStats.BACKGROUND_PROC_STATES, now, null); ProcessStats.BACKGROUND_PROC_STATES, now, null);
final PackageManager pm = getActivity().getPackageManager();
ArrayList<ProcStatsEntry> procs = new ArrayList<ProcStatsEntry>();
for (int i=0, N=(rawProcs != null ? rawProcs.size() : 0); i<N; i++) { for (int i=0, N=(rawProcs != null ? rawProcs.size() : 0); i<N; i++) {
procs.add(new ProcStatsEntry(rawProcs.get(i), totals)); procs.add(new ProcStatsEntry(rawProcs.get(i), totals));
} }
Collections.sort(procs, new Comparator<ProcStatsEntry>() { */
@Override
public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) { for (int ip=0, N=mStats.mProcesses.getMap().size(); ip<N; ip++) {
if (lhs.mWeight < rhs.mWeight) { SparseArray<ProcessStats.ProcessState> uids = mStats.mProcesses.getMap().valueAt(ip);
return 1; for (int iu=0; iu<uids.size(); iu++) {
} else if (lhs.mWeight > rhs.mWeight) { procs.add(new ProcStatsEntry(uids.valueAt(iu), totals));
return -1;
} }
return 0;
} }
});
Collections.sort(procs, sEntryCompare);
while (procs.size() > MAX_ITEMS_TO_LIST) { while (procs.size() > MAX_ITEMS_TO_LIST) {
procs.remove(procs.size()-1); procs.remove(procs.size()-1);
} }
@@ -232,19 +272,56 @@ public class ProcessStatsUi extends PreferenceFragment {
ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null); ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null);
ApplicationInfo targetApp = null; ApplicationInfo targetApp = null;
String label = proc.mName; String label = proc.mName;
String pkgName = null;
if (proc.mUnique) { if (proc.mUnique) {
pkgName = proc.mPackage;
proc.addServices(mStats.getPackageStateLocked(proc.mPackage, proc.mUid));
} else {
// See if there is one significant package that was running here.
ArrayList<ProcStatsEntry> subProcs = new ArrayList<ProcStatsEntry>();
for (int ipkg=0, NPKG=mStats.mPackages.getMap().size(); ipkg<NPKG; ipkg++) {
SparseArray<ProcessStats.PackageState> uids
= mStats.mPackages.getMap().valueAt(ipkg);
for (int iu=0, NU=uids.size(); iu<NU; iu++) {
if (uids.keyAt(iu) != proc.mUid) {
continue;
}
ProcessStats.PackageState pkgState = uids.valueAt(iu);
boolean match = false;
for (int iproc=0, NPROC=pkgState.mProcesses.size(); iproc<NPROC; iproc++) {
ProcessStats.ProcessState subProc =
pkgState.mProcesses.valueAt(iproc);
if (subProc.mName.equals(proc.mName)) {
match = true;
subProcs.add(new ProcStatsEntry(subProc, totals));
}
}
if (match) {
proc.addServices(mStats.getPackageStateLocked(proc.mPackage,
proc.mUid));
}
}
}
if ( subProcs.size() > 1) {
Collections.sort(subProcs, sEntryCompare);
if (subProcs.get(0).mWeight > (subProcs.get(1).mWeight*3)) {
pkgName = subProcs.get(0).mPackage;
}
}
}
if (pkgName != null) {
// Only one app associated with this process. // Only one app associated with this process.
try { try {
targetApp = pm.getApplicationInfo(proc.mPackage, targetApp = pm.getApplicationInfo(pkgName,
PackageManager.GET_DISABLED_COMPONENTS | PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES); PackageManager.GET_UNINSTALLED_PACKAGES);
String name = targetApp.loadLabel(pm).toString(); String name = targetApp.loadLabel(pm).toString();
if (proc.mName.equals(proc.mPackage)) { if (proc.mName.equals(pkgName)) {
label = name; label = name;
} else { } else {
if (proc.mName.startsWith(proc.mPackage)) { if (proc.mName.startsWith(pkgName)) {
int off = proc.mPackage.length(); int off = pkgName.length();
if (proc.mName.length() > off) { if (proc.mName.length() > off) {
off++; off++;
} }
@@ -258,15 +335,15 @@ public class ProcessStatsUi extends PreferenceFragment {
} }
if (targetApp == null) { if (targetApp == null) {
String[] packages = pm.getPackagesForUid(proc.mUid); String[] packages = pm.getPackagesForUid(proc.mUid);
for (String pkgName : packages) { for (String curPkg : packages) {
try { try {
final PackageInfo pi = pm.getPackageInfo(pkgName, final PackageInfo pi = pm.getPackageInfo(curPkg,
PackageManager.GET_DISABLED_COMPONENTS | PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES); PackageManager.GET_UNINSTALLED_PACKAGES);
if (pi.sharedUserLabel != 0) { if (pi.sharedUserLabel != 0) {
targetApp = pi.applicationInfo; targetApp = pi.applicationInfo;
final CharSequence nm = pm.getText(pkgName, final CharSequence nm = pm.getText(curPkg,
pi.sharedUserLabel, pi.applicationInfo); pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) { if (nm != null) {
label = nm.toString() + " (" + proc.mName + ")"; label = nm.toString() + " (" + proc.mName + ")";
@@ -293,6 +370,7 @@ public class ProcessStatsUi extends PreferenceFragment {
private void load() { private void load() {
try { try {
mMemState = mProcessStats.getCurrentMemoryState();
ArrayList<ParcelFileDescriptor> fds = new ArrayList<ParcelFileDescriptor>(); ArrayList<ParcelFileDescriptor> fds = new ArrayList<ParcelFileDescriptor>();
byte[] data = mProcessStats.getCurrentStats(fds); byte[] data = mProcessStats.getCurrentStats(fds);
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
@@ -300,7 +378,7 @@ public class ProcessStatsUi extends PreferenceFragment {
parcel.setDataPosition(0); parcel.setDataPosition(0);
mStats = ProcessStats.CREATOR.createFromParcel(parcel); mStats = ProcessStats.CREATOR.createFromParcel(parcel);
int i = fds.size()-1; int i = fds.size()-1;
while (i > 0 && (mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime) while (i >= 0 && (mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime)
< (24*60*60*1000)) { < (24*60*60*1000)) {
Log.i(TAG, "Not enough data, loading next file @ " + i); Log.i(TAG, "Not enough data, loading next file @ " + i);
ProcessStats stats = new ProcessStats(false); ProcessStats stats = new ProcessStats(false);