Make proc stats UI app-centric instead of process-centric.

The UI now shows a much simpler overview of memory use for
the current measured duration, computing the weighted RAM
user per application.  (This is done basically by rolling up
each individual process into an app that can contain multiple
processes, and using the recently introduced weighted RAM for
all data processing.)

The details screen is updated to reflect this new design,
showing an overview of a particular application, which
separate entries for each process running for that app.

Change-Id: I47d79c30086d733eb37440a6c21b18a92b767d01
This commit is contained in:
Dianne Hackborn
2015-01-30 18:19:38 -08:00
parent 7221a6bcaa
commit b94079a704
8 changed files with 647 additions and 397 deletions

View File

@@ -19,6 +19,7 @@ package com.android.settings.applications;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -50,6 +51,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
public class ProcessStatsUi extends PreferenceFragment
implements LinearColorBar.OnRegionTappedListener {
@@ -75,13 +77,30 @@ public class ProcessStatsUi extends PreferenceFragment
final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
@Override
public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
if (lhs.mWeight < rhs.mWeight) {
if (lhs.mRunWeight < rhs.mRunWeight) {
return 1;
} else if (lhs.mWeight > rhs.mWeight) {
} else if (lhs.mRunWeight > rhs.mRunWeight) {
return -1;
} else if (lhs.mDuration < rhs.mDuration) {
} else if (lhs.mRunDuration < rhs.mRunDuration) {
return 1;
} else if (lhs.mDuration > rhs.mDuration) {
} else if (lhs.mRunDuration > rhs.mRunDuration) {
return -1;
}
return 0;
}
};
final static Comparator<ProcStatsPackageEntry> sPackageEntryCompare
= new Comparator<ProcStatsPackageEntry>() {
@Override
public int compare(ProcStatsPackageEntry lhs, ProcStatsPackageEntry rhs) {
if (lhs.mRunWeight < rhs.mRunWeight) {
return 1;
} else if (lhs.mRunWeight > rhs.mRunWeight) {
return -1;
} else if (lhs.mRunDuration < rhs.mRunDuration) {
return 1;
} else if (lhs.mRunDuration > rhs.mRunDuration) {
return -1;
}
return 0;
@@ -112,7 +131,7 @@ public class ProcessStatsUi extends PreferenceFragment
private PreferenceGroup mAppListGroup;
private Preference mMemStatusPref;
long mMaxWeight;
double mMaxWeight;
long mTotalTime;
long[] mMemTimes = new long[ProcessStats.ADJ_MEM_FACTOR_COUNT];
@@ -123,6 +142,7 @@ public class ProcessStatsUi extends PreferenceFragment
double mMemKernelWeight;
double mMemNativeWeight;
double mMemTotalWeight;
double mWeightToRam;
// The actual duration value to use for each duration option. Note these
// are lower than the actual duration, since our durations are computed in
@@ -218,9 +238,10 @@ public class ProcessStatsUi extends PreferenceFragment
ProcessStatsPreference pgp = (ProcessStatsPreference) preference;
Bundle args = new Bundle();
args.putParcelable(ProcessStatsDetail.EXTRA_ENTRY, pgp.getEntry());
args.putParcelable(ProcessStatsDetail.EXTRA_PACKAGE_ENTRY, pgp.getEntry());
args.putBoolean(ProcessStatsDetail.EXTRA_USE_USS, mUseUss);
args.putLong(ProcessStatsDetail.EXTRA_MAX_WEIGHT, mMaxWeight);
args.putDouble(ProcessStatsDetail.EXTRA_MAX_WEIGHT, mMaxWeight);
args.putDouble(ProcessStatsDetail.EXTRA_WEIGHT_TO_RAM, mWeightToRam);
args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, mTotalTime);
((SettingsActivity) getActivity()).startPreferencePanel(
ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0);
@@ -406,31 +427,6 @@ public class ProcessStatsUi extends PreferenceFragment
final long elapsedTime = mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime;
mMemStatusPref.setOrder(-2);
mAppListGroup.addPreference(mMemStatusPref);
String durationString = Utils.formatElapsedTime(getActivity(), elapsedTime, false);
CharSequence memString;
CharSequence[] memStatesStr = getResources().getTextArray(R.array.ram_states);
if (mMemState >= 0 && mMemState < memStatesStr.length) {
memString = memStatesStr[mMemState];
} else {
memString = "?";
}
mMemStatusPref.setTitle(getActivity().getString(R.string.process_stats_total_duration,
getActivity().getString(statsLabel), durationString));
mMemStatusPref.setSummary(getActivity().getString(R.string.process_stats_memory_status,
memString));
/*
mMemStatusPref.setTitle(DateFormat.format(DateFormat.getBestDateTimePattern(
getActivity().getResources().getConfiguration().locale,
"MMMM dd, yyyy h:mm a"), mStats.mTimePeriodStartClock));
*/
/*
BatteryHistoryPreference hist = new BatteryHistoryPreference(getActivity(), mStats);
hist.setOrder(-1);
mAppListGroup.addPreference(hist);
*/
long now = SystemClock.uptimeMillis();
final PackageManager pm = getActivity().getPackageManager();
@@ -470,9 +466,13 @@ public class ProcessStatsUi extends PreferenceFragment
memStates = ProcessStats.ALL_MEM_ADJ;
break;
}
colors.setColoredRegions(LinearColorBar.REGION_RED);
Resources res = getResources();
colors.setColors(res.getColor(R.color.running_processes_apps_ram),
res.getColor(R.color.running_processes_apps_ram),
res.getColor(R.color.running_processes_free_ram));
// Compute memory badness for chart color.
/*
int[] badColors = com.android.settings.Utils.BADNESS_COLORS;
long timeGood = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_NORMAL];
timeGood += (mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]*2)/3;
@@ -480,6 +480,7 @@ public class ProcessStatsUi extends PreferenceFragment
float memBadness = ((float)timeGood)/mTotalTime;
int badnessColor = badColors[1 + Math.round(memBadness*(badColors.length-2))];
colors.setColors(badnessColor, badnessColor, badnessColor);
*/
// We are now going to scale the mMemTimes to match the total elapsed time.
// These are in uptime, so they will often be smaller than the elapsed time,
@@ -547,6 +548,8 @@ public class ProcessStatsUi extends PreferenceFragment
memReader.readMemInfo();
double realTotalRam = memReader.getTotalSize();
double totalScale = realTotalRam / totalRam;
mWeightToRam = totalScale / memTotalTime * 1024;
mMaxWeight = totalRam / mWeightToRam;
double realUsedRam = usedRam * totalScale;
double realFreeRam = freeRam * totalScale;
if (DEBUG) {
@@ -558,12 +561,15 @@ public class ProcessStatsUi extends PreferenceFragment
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
((ActivityManager)getActivity().getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(
memInfo);
long baseCacheRam;
if (memInfo.hiddenAppThreshold >= realFreeRam) {
realUsedRam = realFreeRam;
realFreeRam = 0;
baseCacheRam = (long)realFreeRam;
} else {
realUsedRam += memInfo.hiddenAppThreshold;
realFreeRam -= memInfo.hiddenAppThreshold;
baseCacheRam = memInfo.hiddenAppThreshold;
}
if (DEBUG) {
Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(getActivity(),
@@ -572,6 +578,22 @@ public class ProcessStatsUi extends PreferenceFragment
(long)realFreeRam));
}
mMemStatusPref.setOrder(-2);
mAppListGroup.addPreference(mMemStatusPref);
String durationString = Utils.formatElapsedTime(getActivity(), elapsedTime, false);
String usedString = Formatter.formatShortFileSize(getActivity(), (long) realUsedRam);
String totalString = Formatter.formatShortFileSize(getActivity(), (long)realTotalRam);
CharSequence memString;
CharSequence[] memStatesStr = getResources().getTextArray(R.array.ram_states);
if (mMemState >= 0 && mMemState < memStatesStr.length) {
memString = memStatesStr[mMemState];
} else {
memString = "?";
}
mMemStatusPref.setTitle(getActivity().getString(R.string.process_stats_total_duration,
usedString, totalString, durationString));
mMemStatusPref.setSummary(getActivity().getString(R.string.process_stats_memory_status,
memString));
float usedRatio = (float)(realUsedRam/(realFreeRam+realUsedRam));
colors.setRatios(usedRatio, 0, 1-usedRatio);
@@ -605,17 +627,20 @@ public class ProcessStatsUi extends PreferenceFragment
mAppListGroup.addPreference(colors);
ProcessStats.ProcessDataCollection totals = new ProcessStats.ProcessDataCollection(
ProcessStats.ProcessDataCollection bgTotals = new ProcessStats.ProcessDataCollection(
ProcessStats.ALL_SCREEN_ADJ, memStates, stats);
ProcessStats.ProcessDataCollection runTotals = new ProcessStats.ProcessDataCollection(
ProcessStats.ALL_SCREEN_ADJ, memStates, ProcessStats.NON_CACHED_PROC_STATES);
ArrayList<ProcStatsEntry> entries = new ArrayList<ProcStatsEntry>();
final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>();
final ArrayList<ProcStatsPackageEntry> pkgEntries = new ArrayList<>();
/*
ArrayList<ProcessStats.ProcessState> rawProcs = mStats.collectProcessesLocked(
ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ,
ProcessStats.BACKGROUND_PROC_STATES, now, null);
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), bgTotals));
}
*/
@@ -640,15 +665,15 @@ public class ProcessStatsUi extends PreferenceFragment
}
ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid);
if (ent == null) {
ent = new ProcStatsEntry(proc, st.mPackageName, totals, mUseUss,
mStatsType == MENU_TYPE_BACKGROUND);
if (ent.mDuration > 0) {
ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals,
mUseUss);
if (ent.mRunWeight > 0) {
if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/"
+ proc.mUid + ": time=" + makeDuration(ent.mDuration) + " ("
+ ((((double)ent.mDuration) / memTotalTime) * 100) + "%)"
+ " pss=" + ent.mAvgPss);
+ proc.mUid + ": time=" + makeDuration(ent.mRunDuration) + " ("
+ ((((double)ent.mRunDuration) / memTotalTime) * 100) + "%)"
+ " pss=" + ent.mAvgRunMem);
entriesMap.put(proc.mName, proc.mUid, ent);
entries.add(ent);
procEntries.add(ent);
}
} else {
ent.addPackage(st.mPackageName);
@@ -672,7 +697,8 @@ public class ProcessStatsUi extends PreferenceFragment
for (int is=0, NS=ps.mServices.size(); is<NS; is++) {
ProcessStats.ServiceState ss = ps.mServices.valueAt(is);
if (ss.mProcessName != null) {
ProcStatsEntry ent = entriesMap.get(ss.mProcessName, uids.keyAt(iu));
ProcStatsEntry ent = entriesMap.get(ss.mProcessName,
uids.keyAt(iu));
if (ent != null) {
if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName
+ "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc "
@@ -689,90 +715,99 @@ public class ProcessStatsUi extends PreferenceFragment
}
}
/*
SparseArray<ArrayMap<String, ProcStatsEntry>> processes
= new SparseArray<ArrayMap<String, ProcStatsEntry>>();
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++) {
ProcessStats.ProcessState st = uids.valueAt(iu);
ProcStatsEntry ent = new ProcStatsEntry(st, totals, mUseUss,
mStatsType == MENU_TYPE_BACKGROUND);
if (ent.mDuration > 0) {
if (DEBUG) Log.d(TAG, "Adding proc " + st.mName + "/" + st.mUid + ": time="
+ makeDuration(ent.mDuration) + " ("
+ ((((double)ent.mDuration) / memTotalTime) * 100) + "%)");
procs.add(ent);
ArrayMap<String, ProcStatsEntry> uidProcs = processes.get(ent.mUid);
if (uidProcs == null) {
uidProcs = new ArrayMap<String, ProcStatsEntry>();
processes.put(ent.mUid, uidProcs);
}
uidProcs.put(ent.mName, ent);
}
// Combine processes into packages.
HashMap<String, ProcStatsPackageEntry> pkgMap = new HashMap<>();
for (int i=procEntries.size()-1; i>=0; i--) {
ProcStatsEntry proc = procEntries.get(i);
proc.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage);
if (pkg == null) {
pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage);
pkgMap.put(proc.mBestTargetPackage, pkg);
pkgEntries.add(pkg);
}
pkg.addEntry(proc);
}
*/
Collections.sort(entries, sEntryCompare);
// Add in fake entry representing the OS itself.
ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os");
pkgMap.put("os", osPkg);
pkgEntries.add(osPkg);
ProcStatsEntry osEntry;
if (totalMem.sysMemNativeWeight > 0) {
osEntry = new ProcStatsEntry("os", 0,
getString(R.string.process_stats_os_native), memTotalTime,
(long)(totalMem.sysMemNativeWeight/memTotalTime));
osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry);
}
if (totalMem.sysMemKernelWeight > 0) {
osEntry = new ProcStatsEntry("os", 0,
getString(R.string.process_stats_os_kernel), memTotalTime,
(long)(totalMem.sysMemKernelWeight/memTotalTime));
osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry);
}
if (totalMem.sysMemZRamWeight > 0) {
osEntry = new ProcStatsEntry("os", 0,
getString(R.string.process_stats_os_zram), memTotalTime,
(long)(totalMem.sysMemZRamWeight/memTotalTime));
osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry);
}
if (baseCacheRam > 0) {
osEntry = new ProcStatsEntry("os", 0,
getString(R.string.process_stats_os_cache), memTotalTime, baseCacheRam/1024);
osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry);
}
long maxWeight = 1;
for (int i=0, N=(entries != null ? entries.size() : 0); i<N; i++) {
ProcStatsEntry proc = entries.get(i);
if (maxWeight < proc.mWeight) {
maxWeight = proc.mWeight;
}
}
if (mStatsType == MENU_TYPE_BACKGROUND) {
mMaxWeight = (long)(mShowSystem ? persBackgroundWeight : backgroundWeight);
if (mMaxWeight < maxWeight) {
mMaxWeight = maxWeight;
}
if (DEBUG) {
Log.i(TAG, "Bar max RAM: " + Formatter.formatShortFileSize(getActivity(),
(mMaxWeight * 1024) / memTotalTime));
}
} else {
mMaxWeight = maxWeight;
for (int i=0, N=pkgEntries.size(); i<N; i++) {
ProcStatsPackageEntry pkg = pkgEntries.get(i);
pkg.updateMetrics();
}
Collections.sort(pkgEntries, sPackageEntryCompare);
// Now collect the per-process information into applications, so that applications
// running as multiple processes will have only one entry representing all of them.
if (DEBUG) Log.d(TAG, "-------------------- BUILDING UI");
// Find where we should stop. Because we have two properties we are looking at,
// we need to go from the back looking for the first place either holds.
int end = entries != null ? entries.size()-1 : -1;
int end = pkgEntries.size()-1;
while (end >= 0) {
ProcStatsEntry proc = entries.get(end);
final double percentOfWeight = (((double)proc.mWeight) / mMaxWeight) * 100;
final double percentOfTime = (((double)proc.mDuration) / memTotalTime) * 100;
if (percentOfWeight >= 1 || percentOfTime >= 25) {
ProcStatsPackageEntry pkg = pkgEntries.get(end);
final double percentOfWeight = (pkg.mRunWeight / mMaxWeight) * 100;
final double percentOfTime = (((double)pkg.mRunDuration) / memTotalTime) * 100;
if (percentOfWeight >= .01 || percentOfTime >= 25) {
break;
}
end--;
}
for (int i=0; i<=end; i++) {
ProcStatsEntry proc = entries.get(i);
final double percentOfWeight = (((double)proc.mWeight) / mMaxWeight) * 100;
final double percentOfTime = (((double)proc.mDuration) / memTotalTime) * 100;
ProcStatsPackageEntry pkg = pkgEntries.get(i);
final double percentOfWeight = (pkg.mRunWeight / mMaxWeight) * 100;
final double percentOfTime = (((double)pkg.mRunDuration) / memTotalTime) * 100;
ProcessStatsPreference pref = new ProcessStatsPreference(getActivity());
pref.init(null, proc);
proc.evaluateTargetPackage(pm, mStats, totals, sEntryCompare, mUseUss,
mStatsType == MENU_TYPE_BACKGROUND);
proc.retrieveUiData(pm);
pref.setTitle(proc.mUiLabel);
if (proc.mUiTargetApp != null) {
pref.setIcon(proc.mUiTargetApp.loadIcon(pm));
pref.init(null, pkg);
pkg.retrieveUiData(getActivity(), pm);
pref.setTitle(pkg.mUiLabel);
if (pkg.mUiTargetApp != null) {
pref.setIcon(pkg.mUiTargetApp.loadIcon(pm));
}
pref.setOrder(i);
pref.setPercent(percentOfWeight, percentOfTime);
pref.setPercent(percentOfWeight, percentOfTime,
(long)(pkg.mRunWeight * mWeightToRam));
mAppListGroup.addPreference(pref);
if (mStatsType == MENU_TYPE_BACKGROUND) {
if (DEBUG) {
Log.i(TAG, "App " + proc.mUiLabel + ": weightedRam="
Log.i(TAG, "App " + pkg.mUiLabel + ": weightedRam="
+ Formatter.formatShortFileSize(getActivity(),
(proc.mWeight * 1024) / memTotalTime)
(long)((pkg.mRunWeight * 1024) / memTotalTime))
+ ", avgRam=" + Formatter.formatShortFileSize(getActivity(),
(proc.mAvgPss*1024)));
(pkg.mAvgRunMem *1024)));
}
}