/* * 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.PackageManager; import android.os.Bundle; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceGroup; import android.util.Log; import android.util.TimeUtils; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.applications.ProcStatsData.MemInfo; import java.util.Collections; import java.util.Comparator; import java.util.List; public class ProcessStatsUi extends ProcessStatsBase { static final String TAG = "ProcessStatsUi"; static final boolean DEBUG = false; private static final String KEY_APP_LIST = "app_list"; private static final int MENU_SHOW_AVG = Menu.FIRST; private static final int MENU_SHOW_MAX = Menu.FIRST + 1; private PreferenceGroup mAppListGroup; private PackageManager mPm; private boolean mShowMax; private MenuItem mMenuAvg; private MenuItem mMenuMax; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); mPm = getActivity().getPackageManager(); addPreferencesFromResource(R.xml.process_stats_ui); mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST); setHasOptionsMenu(true); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); mMenuAvg = menu.add(0, MENU_SHOW_AVG, 0, R.string.sort_avg_use); mMenuMax = menu.add(0, MENU_SHOW_MAX, 0, R.string.sort_max_use); updateMenu(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_SHOW_AVG: case MENU_SHOW_MAX: mShowMax = !mShowMax; refreshUi(); updateMenu(); return true; } return super.onOptionsItemSelected(item); } private void updateMenu() { mMenuMax.setVisible(!mShowMax); mMenuAvg.setVisible(mShowMax); } @Override protected int getMetricsCategory() { return MetricsEvent.APPLICATIONS_PROCESS_STATS_UI; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override public boolean onPreferenceTreeClick(Preference preference) { if (!(preference instanceof ProcessStatsPreference)) { return false; } ProcessStatsPreference pgp = (ProcessStatsPreference) preference; MemInfo memInfo = mStatsManager.getMemInfo(); launchMemoryDetail((SettingsActivity) getActivity(), memInfo, pgp.getEntry(), true); return super.onPreferenceTreeClick(preference); } /** * All states in which we consider a process to be actively running (rather than * something that can be freely killed to reclaim RAM). Note this also includes * the HOME state, because we prioritize home over all cached processes even when * it is in the background, so it is effectively always running from the perspective * of the information we want to show the user here. */ public static final int[] BACKGROUND_AND_SYSTEM_PROC_STATES = new int[] { ProcessStats.STATE_PERSISTENT, ProcessStats.STATE_IMPORTANT_FOREGROUND, ProcessStats.STATE_IMPORTANT_BACKGROUND, ProcessStats.STATE_BACKUP, ProcessStats.STATE_HEAVY_WEIGHT, ProcessStats.STATE_SERVICE, ProcessStats.STATE_SERVICE_RESTARTING, ProcessStats.STATE_RECEIVER, ProcessStats.STATE_HOME }; public static final int[] FOREGROUND_PROC_STATES = new int[] { ProcessStats.STATE_TOP }; public static final int[] CACHED_PROC_STATES = new int[] { ProcessStats.STATE_CACHED_ACTIVITY, ProcessStats.STATE_CACHED_ACTIVITY_CLIENT, ProcessStats.STATE_CACHED_EMPTY }; public static String makeDuration(long time) { StringBuilder sb = new StringBuilder(32); TimeUtils.formatDuration(time, sb); return sb.toString(); } @Override public void refreshUi() { mAppListGroup.removeAll(); mAppListGroup.setOrderingAsAdded(false); mAppListGroup.setTitle(mShowMax ? R.string.maximum_memory_use : R.string.average_memory_use); final Context context = getActivity(); MemInfo memInfo = mStatsManager.getMemInfo(); List<ProcStatsPackageEntry> pkgEntries = mStatsManager.getEntries(); // Update everything and get the absolute maximum of memory usage for scaling. for (int i=0, N=pkgEntries.size(); i<N; i++) { ProcStatsPackageEntry pkg = pkgEntries.get(i); pkg.updateMetrics(); } Collections.sort(pkgEntries, mShowMax ? sMaxPackageEntryCompare : 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"); double maxMemory = mShowMax ? memInfo.realTotalRam : memInfo.usedWeight * memInfo.weightToRam; for (int i = 0; i < pkgEntries.size(); i++) { ProcStatsPackageEntry pkg = pkgEntries.get(i); ProcessStatsPreference pref = new ProcessStatsPreference(getPrefContext()); pkg.retrieveUiData(context, mPm); pref.init(pkg, mPm, maxMemory, memInfo.weightToRam, memInfo.totalScale, !mShowMax); pref.setOrder(i); mAppListGroup.addPreference(pref); } } final static Comparator<ProcStatsPackageEntry> sPackageEntryCompare = new Comparator<ProcStatsPackageEntry>() { @Override public int compare(ProcStatsPackageEntry lhs, ProcStatsPackageEntry rhs) { double rhsWeight = Math.max(rhs.mRunWeight, rhs.mBgWeight); double lhsWeight = Math.max(lhs.mRunWeight, lhs.mBgWeight); if (lhsWeight == rhsWeight) { return 0; } return lhsWeight < rhsWeight ? 1 : -1; } }; final static Comparator<ProcStatsPackageEntry> sMaxPackageEntryCompare = new Comparator<ProcStatsPackageEntry>() { @Override public int compare(ProcStatsPackageEntry lhs, ProcStatsPackageEntry rhs) { double rhsMax = Math.max(rhs.mMaxBgMem, rhs.mMaxRunMem); double lhsMax = Math.max(lhs.mMaxBgMem, lhs.mMaxRunMem); if (lhsMax == rhsMax) { return 0; } return lhsMax < rhsMax ? 1 : -1; } }; }