Implement issue #10895990: Better durations for proc stats
Add new option to select data duration to show, with the default
being 3 hours.
Reworked how menus are updated to remove redundant code.
Switch to using new proc stats service API for retrieving stats,
which is more efficient and won't hit IPC limits.
Fixed layout for details items to ellipsize long items.
Change-Id: I1a0f8a4adf8fc330c2639077323c6379d23ad987
diff --git a/res/layout/power_usage_detail_item_text.xml b/res/layout/power_usage_detail_item_text.xml
index fb8c705..e4aee1b 100644
--- a/res/layout/power_usage_detail_item_text.xml
+++ b/res/layout/power_usage_detail_item_text.xml
@@ -26,7 +26,9 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:singleLine="true"
+ android:ellipsize="middle"
android:layout_alignParentStart="true"
+ android:layout_toStartOf="@+id/value"
android:layout_marginBottom="4dip"
android:layout_marginTop="4dip"
android:layout_marginStart="4dip" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e3e2170..370a811 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3657,6 +3657,16 @@
<string name="process_stats_run_time">Run time</string>
<!-- [CHAR LIMIT=NONE] Subtitle for process stats services list -->
<string name="services_subtitle">Services</string>
+ <!-- [CHAR LIMIT=NONE] Menu for process stats to select duration of stats to show -->
+ <string name="menu_proc_stats_duration">Duration</string>
+ <!-- [CHAR LIMIT=NONE] Menu for process stats to show 3 hours of data -->
+ <string name="menu_duration_3h">3 hours</string>
+ <!-- [CHAR LIMIT=NONE] Menu for process stats to show 3 hours of data -->
+ <string name="menu_duration_6h">6 hours</string>
+ <!-- [CHAR LIMIT=NONE] Menu for process stats to show 3 hours of data -->
+ <string name="menu_duration_12h">12 hours</string>
+ <!-- [CHAR LIMIT=NONE] Menu for process stats to show 3 hours of data -->
+ <string name="menu_duration_1d">1 day</string>
<!-- [CHAR LIMIT=NONE] Menu for process stats to control whether system processes are shown -->
<string name="menu_show_system">Show system</string>
<!-- [CHAR LIMIT=NONE] Menu for process stats to control whether computation should be based
diff --git a/src/com/android/settings/applications/ProcessStatsUi.java b/src/com/android/settings/applications/ProcessStatsUi.java
index 7601309..b7ffcef 100644
--- a/src/com/android/settings/applications/ProcessStatsUi.java
+++ b/src/com/android/settings/applications/ProcessStatsUi.java
@@ -56,15 +56,18 @@
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_MEM_STATUS = "mem_status";
- private static final int MENU_STATS_REFRESH = Menu.FIRST;
- private static final int MENU_SHOW_SYSTEM = Menu.FIRST + 1;
- private static final int MENU_USE_USS = Menu.FIRST + 2;
- private static final int MENU_TYPE_BACKGROUND = Menu.FIRST + 3;
- private static final int MENU_TYPE_FOREGROUND = Menu.FIRST + 4;
- private static final int MENU_TYPE_CACHED = Menu.FIRST + 5;
- private static final int MENU_HELP = Menu.FIRST + 6;
+ private static final int NUM_DURATIONS = 4;
- static final int MAX_ITEMS_TO_LIST = 40;
+ private static final int MENU_STATS_REFRESH = Menu.FIRST;
+ private static final int MENU_DURATION = Menu.FIRST + 1;
+ private static final int MENU_SHOW_SYSTEM = MENU_DURATION + NUM_DURATIONS;
+ private static final int MENU_USE_USS = MENU_SHOW_SYSTEM + 1;
+ private static final int MENU_TYPE_BACKGROUND = MENU_USE_USS + 1;
+ private static final int MENU_TYPE_FOREGROUND = MENU_TYPE_BACKGROUND + 1;
+ private static final int MENU_TYPE_CACHED = MENU_TYPE_FOREGROUND + 1;
+ private static final int MENU_HELP = MENU_TYPE_CACHED + 1;
+
+ static final int MAX_ITEMS_TO_LIST = 60;
final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
@Override
@@ -85,10 +88,13 @@
ProcessStats mStats;
int mMemState;
+ private long mDuration;
+ private long mLastDuration;
private boolean mShowSystem;
private boolean mUseUss;
private int mStatsType;
+ private MenuItem[] mDurationMenus = new MenuItem[NUM_DURATIONS];
private MenuItem mShowSystemMenu;
private MenuItem mUseUssMenu;
private MenuItem mTypeBackgroundMenu;
@@ -101,6 +107,21 @@
long mMaxWeight;
long mTotalTime;
+ // The actual duration value to use for each duration option. Note these
+ // are lower than the actual duration, since our durations are computed in
+ // batches of 3 hours so we want to allow the time we use to be slightly
+ // smaller than the actual time selected instead of bumping up to 3 hours
+ // beyond it.
+ private static final long DURATION_QUANTUM = 3*60*60*1000;
+ private static long[] sDurations = new long[] {
+ 3*60*60*1000 - DURATION_QUANTUM/2, 6*60*60*1000 - DURATION_QUANTUM/2,
+ 12*60*60*1000 - DURATION_QUANTUM/2, 24*60*60*1000 - DURATION_QUANTUM/2
+ };
+ private static int[] sDurationLabels = new int[] {
+ R.string.menu_duration_3h, R.string.menu_duration_6h,
+ R.string.menu_duration_12h, R.string.menu_duration_1d
+ };
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -115,6 +136,7 @@
mUm = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
mMemStatusPref = mAppListGroup.findPreference(KEY_MEM_STATUS);
+ mDuration = icicle != null ? icicle.getLong("duration", sDurations[0]) : sDurations[0];
mShowSystem = icicle != null ? icicle.getBoolean("show_system") : false;
mUseUss = icicle != null ? icicle.getBoolean("use_uss") : false;
mStatsType = icicle != null ? icicle.getInt("stats_type", MENU_TYPE_BACKGROUND)
@@ -136,6 +158,7 @@
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
+ outState.putLong("duration", mDuration);
outState.putBoolean("show_system", mShowSystem);
outState.putBoolean("use_uss", mUseUss);
outState.putInt("stats_type", mStatsType);
@@ -174,31 +197,31 @@
.setAlphabeticShortcut('r');
refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ SubMenu subMenu = menu.addSubMenu(R.string.menu_proc_stats_duration);
+ for (int i=0; i<NUM_DURATIONS; i++) {
+ mDurationMenus[i] = subMenu.add(0, MENU_DURATION+i, 0, sDurationLabels[i])
+ .setCheckable(true);
+ }
mShowSystemMenu = menu.add(0, MENU_SHOW_SYSTEM, 0, R.string.menu_show_system)
.setAlphabeticShortcut('s')
- .setCheckable(true)
- .setChecked(mShowSystem)
- .setEnabled(mStatsType == MENU_TYPE_BACKGROUND);
+ .setCheckable(true);
mUseUssMenu = menu.add(0, MENU_USE_USS, 0, R.string.menu_use_uss)
- .setAlphabeticShortcut('s')
- .setCheckable(true)
- .setChecked(mUseUss);
- SubMenu subMenu = menu.addSubMenu(R.string.menu_proc_stats_type);
+ .setAlphabeticShortcut('u')
+ .setCheckable(true);
+ subMenu = menu.addSubMenu(R.string.menu_proc_stats_type);
mTypeBackgroundMenu = subMenu.add(0, MENU_TYPE_BACKGROUND, 0,
R.string.menu_proc_stats_type_background)
.setAlphabeticShortcut('b')
- .setCheckable(true)
- .setChecked(mStatsType == MENU_TYPE_BACKGROUND);
+ .setCheckable(true);
mTypeForegroundMenu = subMenu.add(0, MENU_TYPE_FOREGROUND, 0,
R.string.menu_proc_stats_type_foreground)
.setAlphabeticShortcut('f')
- .setCheckable(true)
- .setChecked(mStatsType == MENU_TYPE_FOREGROUND);
+ .setCheckable(true);
mTypeCachedMenu = subMenu.add(0, MENU_TYPE_CACHED, 0,
R.string.menu_proc_stats_type_cached)
- .setAlphabeticShortcut('c')
- .setCheckable(true)
- .setChecked(mStatsType == MENU_TYPE_CACHED);
+ .setCheckable(true);
+
+ updateMenus();
/*
String helpUrl;
@@ -209,9 +232,44 @@
*/
}
+ void updateMenus() {
+ int closestIndex = 0;
+ long closestDelta = Math.abs(sDurations[0]-mDuration);
+ for (int i=1; i<NUM_DURATIONS; i++) {
+ long delta = Math.abs(sDurations[i]-mDuration);
+ if (delta < closestDelta) {
+ closestDelta = delta;
+ closestIndex = i;
+ }
+ }
+ for (int i=0; i<NUM_DURATIONS; i++) {
+ if (mDurationMenus[i] != null) {
+ mDurationMenus[i].setChecked(i == closestIndex);
+ }
+ }
+ mDuration = sDurations[closestIndex];
+ if (mShowSystemMenu != null) {
+ mShowSystemMenu.setChecked(mShowSystem);
+ mShowSystemMenu.setEnabled(mStatsType == MENU_TYPE_BACKGROUND);
+ }
+ if (mUseUssMenu != null) {
+ mUseUssMenu.setChecked(mUseUss);
+ }
+ if (mTypeBackgroundMenu != null) {
+ mTypeBackgroundMenu.setChecked(mStatsType == MENU_TYPE_BACKGROUND);
+ }
+ if (mTypeForegroundMenu != null) {
+ mTypeForegroundMenu.setChecked(mStatsType == MENU_TYPE_FOREGROUND);
+ }
+ if (mTypeCachedMenu != null) {
+ mTypeCachedMenu.setChecked(mStatsType == MENU_TYPE_CACHED);
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
+ final int id = item.getItemId();
+ switch (id) {
case MENU_STATS_REFRESH:
mStats = null;
refreshStats();
@@ -231,6 +289,10 @@
refreshStats();
return true;
default:
+ if (id >= MENU_DURATION && id < (MENU_DURATION+NUM_DURATIONS)) {
+ mDuration = sDurations[id-MENU_DURATION];
+ refreshStats();
+ }
return false;
}
}
@@ -258,25 +320,10 @@
};
private void refreshStats() {
- if (mStats == null) {
- load();
- }
+ updateMenus();
- if (mShowSystemMenu != null) {
- mShowSystemMenu.setChecked(mShowSystem);
- mShowSystemMenu.setEnabled(mStatsType == MENU_TYPE_BACKGROUND);
- }
- if (mUseUssMenu != null) {
- mUseUssMenu.setChecked(mUseUss);
- }
- if (mTypeBackgroundMenu != null) {
- mTypeBackgroundMenu.setChecked(mStatsType == MENU_TYPE_BACKGROUND);
- }
- if (mTypeForegroundMenu != null) {
- mTypeForegroundMenu.setChecked(mStatsType == MENU_TYPE_FOREGROUND);
- }
- if (mTypeCachedMenu != null) {
- mTypeCachedMenu.setChecked(mStatsType == MENU_TYPE_CACHED);
+ if (mStats == null || mLastDuration != mDuration) {
+ load();
}
int[] stats;
@@ -390,7 +437,7 @@
ProcStatsEntry proc = procs.get(i);
final double percentOfWeight = (((double)proc.mWeight) / maxWeight) * 100;
final double percentOfTime = (((double)proc.mDuration) / mTotalTime) * 100;
- if (percentOfWeight < 2) break;
+ if (percentOfWeight < 1) break;
ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null, proc);
proc.evaluateTargetPackage(mStats, totals, sEntryCompare, mUseUss,
mStatsType == MENU_TYPE_BACKGROUND);
@@ -425,44 +472,18 @@
private void load() {
try {
+ mLastDuration = mDuration;
mMemState = mProcessStats.getCurrentMemoryState();
- ArrayList<ParcelFileDescriptor> fds = new ArrayList<ParcelFileDescriptor>();
- byte[] data = mProcessStats.getCurrentStats(fds);
- Parcel parcel = Parcel.obtain();
- parcel.unmarshall(data, 0, data.length);
- parcel.setDataPosition(0);
- mStats = ProcessStats.CREATOR.createFromParcel(parcel);
- int i = fds.size()-1;
- 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);
- InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(fds.get(i));
- stats.read(stream);
- try {
- stream.close();
- } catch (IOException e) {
- }
- if (stats.mReadError == null) {
- mStats.add(stats);
- StringBuilder sb = new StringBuilder();
- sb.append("Added stats: ");
- sb.append(stats.mTimePeriodStartClockStr);
- sb.append(", over ");
- TimeUtils.formatDuration(
- stats.mTimePeriodEndRealtime-stats.mTimePeriodStartRealtime, sb);
- Log.i(TAG, sb.toString());
- } else {
- Log.w(TAG, "Read error: " + stats.mReadError);
- }
- i--;
+ ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
+ mStats = new ProcessStats(false);
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ mStats.read(is);
+ try {
+ is.close();
+ } catch (IOException e) {
}
- while (i >= 0) {
- try {
- fds.get(i).close();
- } catch (IOException e) {
- }
- i--;
+ if (mStats.mReadError != null) {
+ Log.w(TAG, "Failure reading process stats: " + mStats.mReadError);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException:", e);