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
diff --git a/res/layout/preference_linearcolor.xml b/res/layout/preference_linearcolor.xml
new file mode 100644
index 0000000..bbaf7a1
--- /dev/null
+++ b/res/layout/preference_linearcolor.xml
@@ -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>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 72a6b15..4498115 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1149,4 +1149,16 @@
<item>Always allow</item>
</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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b787c8d..cdf0243 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3557,7 +3557,10 @@
<!-- [CHAR LIMIT=NONE] Label for amount of memory use -->
<string name="app_memory_use">Memory use</string>
<!-- [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 />
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to speech functionality -->
diff --git a/src/com/android/settings/applications/LinearColorBar.java b/src/com/android/settings/applications/LinearColorBar.java
index 74fb02e..8343595 100644
--- a/src/com/android/settings/applications/LinearColorBar.java
+++ b/src/com/android/settings/applications/LinearColorBar.java
@@ -23,6 +23,11 @@
private float mYellowRatio;
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;
final Rect mRect = new Rect();
@@ -57,6 +62,20 @@
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) {
if (mShowingGreen != showingGreen) {
mShowingGreen = showingGreen;
@@ -70,12 +89,15 @@
if (off < 0) off = 0;
mRect.top = off;
mRect.bottom = getHeight();
+ if (!mShowIndicator) {
+ return;
+ }
if (mShowingGreen) {
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 {
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(
0, 0, 0, off/2, 0x00a0a0a0, 0xffa0a0a0, Shader.TileMode.CLAMP));
@@ -111,7 +133,7 @@
if (mLastInterestingLeft != indicatorLeft || mLastInterestingRight != indicatorRight) {
mColorPath.reset();
mEdgePath.reset();
- if (indicatorLeft < indicatorRight) {
+ if (mShowIndicator && indicatorLeft < indicatorRight) {
final int midTopY = mRect.top;
final int midBottomY = 0;
final int xoff = 2;
@@ -146,7 +168,7 @@
if (left < right) {
mRect.left = left;
mRect.right = right;
- mPaint.setColor(LEFT_COLOR);
+ mPaint.setColor(mLeftColor);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
@@ -157,7 +179,7 @@
if (left < right) {
mRect.left = left;
mRect.right = right;
- mPaint.setColor(MIDDLE_COLOR);
+ mPaint.setColor(mMiddleColor);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
@@ -168,7 +190,7 @@
if (left < right) {
mRect.left = left;
mRect.right = right;
- mPaint.setColor(RIGHT_COLOR);
+ mPaint.setColor(mRightColor);
canvas.drawRect(mRect, mPaint);
}
}
diff --git a/src/com/android/settings/applications/LinearColorPreference.java b/src/com/android/settings/applications/LinearColorPreference.java
new file mode 100644
index 0000000..6d9a399
--- /dev/null
+++ b/src/com/android/settings/applications/LinearColorPreference.java
@@ -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);
+ }
+}
diff --git a/src/com/android/settings/applications/ProcStatsEntry.java b/src/com/android/settings/applications/ProcStatsEntry.java
index dec283f..e180205 100644
--- a/src/com/android/settings/applications/ProcStatsEntry.java
+++ b/src/com/android/settings/applications/ProcStatsEntry.java
@@ -18,7 +18,9 @@
import com.android.internal.app.ProcessStats;
-public class ProcStatsEntry {
+import java.util.ArrayList;
+
+public final class ProcStatsEntry {
final String mPackage;
final int mUid;
final String mName;
@@ -27,7 +29,10 @@
final long mAvgPss;
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);
mPackage = proc.mPackage;
mUid = proc.mUid;
@@ -37,4 +42,35 @@
mAvgPss = tmpTotals.avgPss;
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);
+ }
+ }
}
diff --git a/src/com/android/settings/applications/ProcessStatsUi.java b/src/com/android/settings/applications/ProcessStatsUi.java
index 3632288..ec49e0d 100644
--- a/src/com/android/settings/applications/ProcessStatsUi.java
+++ b/src/com/android/settings/applications/ProcessStatsUi.java
@@ -33,6 +33,7 @@
import android.preference.PreferenceScreen;
import android.text.format.DateFormat;
import android.util.Log;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.Menu;
import android.view.MenuInflater;
@@ -60,11 +61,24 @@
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;
IProcessStats mProcessStats;
UserManager mUm;
ProcessStats mStats;
+ int mMemState;
private PreferenceGroup mAppListGroup;
private Preference mMemStatusPref;
@@ -169,8 +183,17 @@
mAppListGroup.addPreference(mMemStatusPref);
String durationString = Utils.formatElapsedTime(getActivity(),
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,
durationString));
+ mMemStatusPref.setSummary(getActivity().getString(R.string.process_stats_memory_status,
+ memString));
/*
mMemStatusPref.setTitle(DateFormat.format(DateFormat.getBestDateTimePattern(
getActivity().getResources().getConfiguration().locale,
@@ -188,30 +211,47 @@
long now = SystemClock.uptimeMillis();
+ final PackageManager pm = getActivity().getPackageManager();
+
mTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations,
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(
ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ,
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++) {
procs.add(new ProcStatsEntry(rawProcs.get(i), totals));
}
- Collections.sort(procs, 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;
+ */
+
+ for (int ip=0, N=mStats.mProcesses.getMap().size(); ip<N; ip++) {
+ SparseArray<ProcessStats.ProcessState> uids = mStats.mProcesses.getMap().valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ procs.add(new ProcStatsEntry(uids.valueAt(iu), totals));
}
- });
+ }
+
+ Collections.sort(procs, sEntryCompare);
while (procs.size() > MAX_ITEMS_TO_LIST) {
procs.remove(procs.size()-1);
}
@@ -232,19 +272,56 @@
ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null);
ApplicationInfo targetApp = null;
String label = proc.mName;
+ String pkgName = null;
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.
try {
- targetApp = pm.getApplicationInfo(proc.mPackage,
+ targetApp = pm.getApplicationInfo(pkgName,
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES);
String name = targetApp.loadLabel(pm).toString();
- if (proc.mName.equals(proc.mPackage)) {
+ if (proc.mName.equals(pkgName)) {
label = name;
} else {
- if (proc.mName.startsWith(proc.mPackage)) {
- int off = proc.mPackage.length();
+ if (proc.mName.startsWith(pkgName)) {
+ int off = pkgName.length();
if (proc.mName.length() > off) {
off++;
}
@@ -258,15 +335,15 @@
}
if (targetApp == null) {
String[] packages = pm.getPackagesForUid(proc.mUid);
- for (String pkgName : packages) {
+ for (String curPkg : packages) {
try {
- final PackageInfo pi = pm.getPackageInfo(pkgName,
+ final PackageInfo pi = pm.getPackageInfo(curPkg,
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES);
if (pi.sharedUserLabel != 0) {
targetApp = pi.applicationInfo;
- final CharSequence nm = pm.getText(pkgName,
+ final CharSequence nm = pm.getText(curPkg,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
label = nm.toString() + " (" + proc.mName + ")";
@@ -293,6 +370,7 @@
private void load() {
try {
+ mMemState = mProcessStats.getCurrentMemoryState();
ArrayList<ParcelFileDescriptor> fds = new ArrayList<ParcelFileDescriptor>();
byte[] data = mProcessStats.getCurrentStats(fds);
Parcel parcel = Parcel.obtain();
@@ -300,7 +378,7 @@
parcel.setDataPosition(0);
mStats = ProcessStats.CREATOR.createFromParcel(parcel);
int i = fds.size()-1;
- while (i > 0 && (mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime)
+ while (i >= 0 && (mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime)
< (24*60*60*1000)) {
Log.i(TAG, "Not enough data, loading next file @ " + i);
ProcessStats stats = new ProcessStats(false);