From c6d658e37d7c0ab2d264fff4850ea20823669558 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 8 Aug 2013 12:57:53 -0700 Subject: [PATCH] Initial rough stab at a proc stats UI. You see procs, and you see some stats. Change-Id: I91c86d198fd5e64a41c72901e150466b3c602caf --- res/values/strings.xml | 10 + res/xml/development_prefs.xml | 6 + res/xml/process_stats_summary.xml | 25 ++ .../android/settings/DevelopmentSettings.java | 2 + src/com/android/settings/Settings.java | 4 +- .../applications/ProcessStatsPreference.java | 55 ++++ .../settings/applications/ProcessStatsUi.java | 269 ++++++++++++++++++ 7 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 res/xml/process_stats_summary.xml create mode 100644 src/com/android/settings/applications/ProcessStatsPreference.java create mode 100644 src/com/android/settings/applications/ProcessStatsUi.java diff --git a/res/values/strings.xml b/res/values/strings.xml index b3a68ae404b..43a817d1b86 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3545,6 +3545,16 @@ Mediaserver + + + + + Process Stats + + Geeky stats about running processes + + Memory use + Voice input & output diff --git a/res/xml/development_prefs.xml b/res/xml/development_prefs.xml index 3a473bb8303..fa244956d06 100644 --- a/res/xml/development_prefs.xml +++ b/res/xml/development_prefs.xml @@ -61,6 +61,12 @@ android:title="@string/bt_hci_snoop_log" android:summary="@string/bt_hci_snoop_log_summary"/> + + + diff --git a/res/xml/process_stats_summary.xml b/res/xml/process_stats_summary.xml new file mode 100644 index 00000000000..fbd87fd13b7 --- /dev/null +++ b/res/xml/process_stats_summary.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java index 66f024e2918..11ffbf72068 100644 --- a/src/com/android/settings/DevelopmentSettings.java +++ b/src/com/android/settings/DevelopmentSettings.java @@ -1283,6 +1283,8 @@ public class DevelopmentSettings extends RestrictedSettingsFragment writeDebugLayoutOptions(); } else if (preference == mForceRtlLayout) { writeForceRtlOptions(); + } else { + return super.onPreferenceTreeClick(preferenceScreen, preference); } return false; diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index f5dbd3bdbf5..a9d35a2ef9a 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -64,6 +64,7 @@ import com.android.settings.accounts.AuthenticatorHelper; import com.android.settings.accounts.ManageAccountsSettings; import com.android.settings.applications.AppOpsSummary; import com.android.settings.applications.ManageApplications; +import com.android.settings.applications.ProcessStatsUi; import com.android.settings.bluetooth.BluetoothEnabler; import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.deviceinfo.Memory; @@ -310,6 +311,7 @@ public class Settings extends PreferenceActivity DisplaySettings.class.getName(), DeviceInfoSettings.class.getName(), ManageApplications.class.getName(), + ProcessStatsUi.class.getName(), NotificationStation.class.getName(), AppOpsSummary.class.getName(), LocationSettings.class.getName(), @@ -331,7 +333,7 @@ public class Settings extends PreferenceActivity UserSettings.class.getName(), NotificationAccessSettings.class.getName(), ManageAccountsSettings.class.getName(), - PrintingSettings.class.getName() + PrintingSettings.class.getName(), }; @Override diff --git a/src/com/android/settings/applications/ProcessStatsPreference.java b/src/com/android/settings/applications/ProcessStatsPreference.java new file mode 100644 index 00000000000..1b9e85207d3 --- /dev/null +++ b/src/com/android/settings/applications/ProcessStatsPreference.java @@ -0,0 +1,55 @@ +/* + * 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.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.preference.Preference; +import android.text.format.Formatter; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import com.android.settings.R; + +public class ProcessStatsPreference extends Preference { + private int mProgress; + private CharSequence mProgressText; + + public ProcessStatsPreference(Context context, Drawable icon) { + super(context); + setLayoutResource(R.layout.app_percentage_item); + setIcon(icon != null ? icon : new ColorDrawable(0)); + } + + public void setPercent(double percentOfTotal, long pss) { + mProgress = (int) Math.ceil(percentOfTotal); + mProgressText = pss > 0 ? Formatter.formatShortFileSize(getContext(), pss) : ""; + notifyChanged(); + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + + final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress); + progress.setProgress(mProgress); + + final TextView text1 = (TextView) view.findViewById(android.R.id.text1); + text1.setText(mProgressText); + } +} diff --git a/src/com/android/settings/applications/ProcessStatsUi.java b/src/com/android/settings/applications/ProcessStatsUi.java new file mode 100644 index 00000000000..6434e9efe60 --- /dev/null +++ b/src/com/android/settings/applications/ProcessStatsUi.java @@ -0,0 +1,269 @@ +/* + * 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.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserManager; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceGroup; +import android.preference.PreferenceScreen; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import com.android.internal.app.IProcessStats; +import com.android.internal.app.ProcessStats; +import com.android.settings.R; + +import java.io.IOException; +import java.util.ArrayList; + +public class ProcessStatsUi extends PreferenceFragment { + private static final String TAG = "ProcessStatsUi"; + private static final boolean DEBUG = false; + + 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_HELP = Menu.FIRST + 2; + + static final int MAX_ITEMS_TO_LIST = 20; + + private static ProcessStats sStatsXfer; + + IProcessStats mProcessStats; + UserManager mUm; + ProcessStats mStats; + + private PreferenceGroup mAppListGroup; + private Preference mMemStatusPref; + + long mTotalTime; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + if (icicle != null) { + mStats = sStatsXfer; + } + + addPreferencesFromResource(R.xml.process_stats_summary); + mProcessStats = IProcessStats.Stub.asInterface( + ServiceManager.getService(ProcessStats.SERVICE_NAME)); + mUm = (UserManager)getActivity().getSystemService(Context.USER_SERVICE); + mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST); + mMemStatusPref = mAppListGroup.findPreference(KEY_MEM_STATUS); + setHasOptionsMenu(true); + } + + @Override + public void onResume() { + super.onResume(); + refreshStats(); + } + + @Override + public void onPause() { + super.onPause(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (getActivity().isChangingConfigurations()) { + sStatsXfer = mStats; + } + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + if (!(preference instanceof ProcessStatsPreference)) { + return false; + } + + /* + PreferenceActivity pa = (PreferenceActivity)getActivity(); + pa.startPreferencePanel(PowerUsageDetail.class.getName(), args, + R.string.details_title, null, null, 0); + */ + + return super.onPreferenceTreeClick(preferenceScreen, preference); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + MenuItem refresh = menu.add(0, MENU_STATS_REFRESH, 0, R.string.menu_stats_refresh) + .setIcon(R.drawable.ic_menu_refresh_holo_dark) + .setAlphabeticShortcut('r'); + refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | + MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + /* + String helpUrl; + if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_battery))) { + final MenuItem help = menu.add(0, MENU_HELP, 0, R.string.help_label); + HelpUtils.prepareHelpMenuItem(getActivity(), help, helpUrl); + } + */ + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_STATS_REFRESH: + mStats = null; + refreshStats(); + return true; + default: + return false; + } + } + + private void addNotAvailableMessage() { + Preference notAvailable = new Preference(getActivity()); + notAvailable.setTitle(R.string.power_usage_not_available); + mAppListGroup.addPreference(notAvailable); + } + + private void refreshStats() { + if (mStats == null) { + load(); + } + + mAppListGroup.removeAll(); + mAppListGroup.setOrderingAsAdded(false); + + mMemStatusPref.setOrder(-2); + mAppListGroup.addPreference(mMemStatusPref); + /* + BatteryHistoryPreference hist = new BatteryHistoryPreference(getActivity(), mStats); + hist.setOrder(-1); + mAppListGroup.addPreference(hist); + */ + + ProcessStats.ProcessDataCollection totals = new ProcessStats.ProcessDataCollection( + ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ, + ProcessStats.BACKGROUND_PROC_STATES); + + long now = SystemClock.uptimeMillis(); + + mTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations, + mStats.mMemFactor, mStats.mStartTime, now); + + ArrayList procs = mStats.collectProcessesLocked( + ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ, + ProcessStats.BACKGROUND_PROC_STATES, now, null); + + final PackageManager pm = getActivity().getPackageManager(); + + for (int i=0, N=(procs != null ? procs.size() : 0); i off) { + off++; + } + label = name + " (" + ps.mName.substring(off) + ")"; + } else { + label = name + " (" + ps.mName + ")"; + } + } + } catch (PackageManager.NameNotFoundException e) { + } + } + if (targetApp == null) { + String[] packages = pm.getPackagesForUid(ps.mUid); + for (String pkgName : packages) { + try { + final PackageInfo pi = pm.getPackageInfo(pkgName, + 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, + pi.sharedUserLabel, pi.applicationInfo); + if (nm != null) { + label = nm.toString() + " (" + ps.mName + ")"; + } else { + label = targetApp.loadLabel(pm).toString() + " (" + ps.mName + ")"; + } + break; + } + } catch (PackageManager.NameNotFoundException e) { + } + } + } + pref.setTitle(label); + if (targetApp != null) { + pref.setIcon(targetApp.loadIcon(pm)); + } + pref.setOrder(N+100-i); + ProcessStats.computeProcessData(ps, totals, now); + pref.setPercent(percentOfTotal, totals.avgPss * 1024); + mAppListGroup.addPreference(pref); + if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) break; + } + } + + private void load() { + try { + ArrayList fds = new ArrayList(); + byte[] data = mProcessStats.getCurrentStats(fds); + for (int i=0; i