2 * Copyright (C) 2013 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings.applications;
19 import android.app.ActivityManager;
20 import android.app.ActivityManager.RunningServiceInfo;
21 import android.app.AlertDialog;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageManager.NameNotFoundException;
30 import android.content.pm.ServiceInfo;
31 import android.graphics.drawable.ColorDrawable;
32 import android.os.Bundle;
33 import android.os.Process;
34 import android.preference.PreferenceCategory;
35 import android.text.TextUtils;
36 import android.text.format.Formatter;
37 import android.util.ArrayMap;
38 import android.util.Log;
39 import android.view.View;
40 import android.widget.Button;
41 import android.widget.TextView;
43 import com.android.internal.logging.MetricsLogger;
44 import com.android.settings.AppHeader;
45 import com.android.settings.CancellablePreference;
46 import com.android.settings.CancellablePreference.OnCancelListener;
47 import com.android.settings.R;
48 import com.android.settings.SettingsPreferenceFragment;
49 import com.android.settings.applications.ProcStatsEntry.Service;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.Comparator;
54 import java.util.HashMap;
55 import java.util.List;
57 public class ProcessStatsDetail extends SettingsPreferenceFragment
58 implements Button.OnClickListener {
60 private static final String TAG = "ProcessStatsDetail";
62 public static final int ACTION_FORCE_STOP = 1;
64 public static final String EXTRA_PACKAGE_ENTRY = "package_entry";
65 public static final String EXTRA_USE_USS = "use_uss";
66 public static final String EXTRA_WEIGHT_TO_RAM = "weight_to_ram";
67 public static final String EXTRA_TOTAL_TIME = "total_time";
68 public static final String EXTRA_MAX_MEMORY_USAGE = "max_memory_usage";
69 public static final String EXTRA_TOTAL_SCALE = "total_scale";
71 private static final String KEY_DETAILS_HEADER = "details_header";
73 private final ArrayMap<ComponentName, CancellablePreference> mServiceMap = new ArrayMap<>();
75 private PackageManager mPm;
76 private DevicePolicyManager mDpm;
78 private ProcStatsPackageEntry mApp;
79 private boolean mUseUss;
80 private double mWeightToRam;
81 private long mTotalTime;
82 private long mOnePercentTime;
84 private Button mForceStopButton;
85 private Button mReportButton;
87 private LinearColorBar mColorBar;
89 private double mMaxMemoryUsage;
91 private double mTotalScale;
94 public void onCreate(Bundle icicle) {
95 super.onCreate(icicle);
96 mPm = getActivity().getPackageManager();
97 mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
98 final Bundle args = getArguments();
99 mApp = args.getParcelable(EXTRA_PACKAGE_ENTRY);
100 mApp.retrieveUiData(getActivity(), mPm);
101 mUseUss = args.getBoolean(EXTRA_USE_USS);
102 mWeightToRam = args.getDouble(EXTRA_WEIGHT_TO_RAM);
103 mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
104 mMaxMemoryUsage = args.getDouble(EXTRA_MAX_MEMORY_USAGE);
105 mTotalScale = args.getDouble(EXTRA_TOTAL_SCALE);
106 mOnePercentTime = mTotalTime/100;
113 public void onViewCreated(View view, Bundle savedInstanceState) {
114 super.onViewCreated(view, savedInstanceState);
116 AppHeader.createAppHeader(this,
117 mApp.mUiTargetApp != null ? mApp.mUiTargetApp.loadIcon(mPm) : new ColorDrawable(0),
118 mApp.mUiLabel, AppInfoWithHeader.getInfoIntent(this, mApp.mPackage));
122 protected int getMetricsCategory() {
123 return MetricsLogger.APPLICATIONS_PROCESS_STATS_DETAIL;
127 public void onResume() {
131 updateRunningServices();
134 private void updateRunningServices() {
135 ActivityManager activityManager = (ActivityManager)
136 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
137 List<RunningServiceInfo> runningServices =
138 activityManager.getRunningServices(Integer.MAX_VALUE);
140 // Set all services as not running, then turn back on the ones we find.
141 int N = mServiceMap.size();
142 for (int i = 0; i < N; i++) {
143 mServiceMap.valueAt(i).setCancellable(false);
146 N = runningServices.size();
147 for (int i = 0; i < N; i++) {
148 RunningServiceInfo runningService = runningServices.get(i);
149 if (!runningService.started && runningService.clientLabel == 0) {
152 if ((runningService.flags & RunningServiceInfo.FLAG_PERSISTENT_PROCESS) != 0) {
155 final ComponentName service = runningService.service;
156 CancellablePreference pref = mServiceMap.get(service);
158 pref.setOnCancelListener(new OnCancelListener() {
160 public void onCancel(CancellablePreference preference) {
161 stopService(service.getPackageName(), service.getClassName());
164 pref.setCancellable(true);
169 private void createDetails() {
170 addPreferencesFromResource(R.xml.app_memory_settings);
172 fillProcessesSection();
174 LayoutPreference headerLayout = (LayoutPreference) findPreference(KEY_DETAILS_HEADER);
176 TextView avgUsed = (TextView) headerLayout.findViewById(R.id.memory_avg);
177 TextView maxUsed = (TextView) headerLayout.findViewById(R.id.memory_max);
178 avgUsed.setText(getString(R.string.memory_avg_desc,
179 Formatter.formatShortFileSize(getActivity(),
180 (long) (Math.max(mApp.mBgWeight, mApp.mRunWeight) * mWeightToRam))));
181 maxUsed.setText(getString(R.string.memory_max_desc,
182 Formatter.formatShortFileSize(getActivity(),
183 (long) (Math.max(mApp.mMaxBgMem, mApp.mMaxRunMem) * 1024 * mTotalScale))));
185 mForceStopButton = (Button) headerLayout.findViewById(R.id.right_button);
186 mReportButton = (Button) headerLayout.findViewById(R.id.left_button);
188 if (mApp.mEntries.get(0).mUid >= android.os.Process.FIRST_APPLICATION_UID) {
189 mForceStopButton.setEnabled(false);
190 mReportButton.setVisibility(View.INVISIBLE);
192 mForceStopButton.setText(R.string.force_stop);
193 mForceStopButton.setTag(ACTION_FORCE_STOP);
194 mForceStopButton.setOnClickListener(this);
196 mReportButton.setVisibility(View.GONE);
197 mForceStopButton.setVisibility(View.GONE);
200 // TODO: Find way to share this code with ProcessStatsPreference.
201 boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight;
202 float avgRatio = (float) ((statsForeground ? mApp.mRunWeight : mApp.mBgWeight)
203 * mWeightToRam / mMaxMemoryUsage);
204 float maxRatio = (float) ((statsForeground ? mApp.mMaxRunMem : mApp.mMaxBgMem)
205 * mTotalScale * 1024 / mMaxMemoryUsage - avgRatio);
206 float remainingRatio = 1 - avgRatio - maxRatio;
207 mColorBar = (LinearColorBar) headerLayout.findViewById(R.id.color_bar);
208 Context context = getActivity();
209 mColorBar.setColors(context.getColor(R.color.memory_avg_use),
210 context.getColor(R.color.memory_max_use),
211 context.getColor(R.color.memory_remaining));
212 mColorBar.setRatios(avgRatio, maxRatio, remainingRatio);
215 public void onClick(View v) {
216 doAction((Integer) v.getTag());
219 private void doAction(int action) {
221 case ACTION_FORCE_STOP:
227 final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
229 public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
230 if (lhs.mRunWeight < rhs.mRunWeight) {
232 } else if (lhs.mRunWeight > rhs.mRunWeight) {
239 private void fillProcessesSection() {
240 final ArrayList<ProcStatsEntry> entries = new ArrayList<>();
241 for (int ie = 0; ie < mApp.mEntries.size(); ie++) {
242 ProcStatsEntry entry = mApp.mEntries.get(ie);
243 if (entry.mPackage.equals("os")) {
244 entry.mLabel = entry.mName;
246 entry.mLabel = getProcessName(mApp.mUiLabel, entry);
250 Collections.sort(entries, sEntryCompare);
251 for (int ie = 0; ie < entries.size(); ie++) {
252 ProcStatsEntry entry = entries.get(ie);
253 PreferenceCategory processPref = new PreferenceCategory(getActivity());
254 processPref.setLayoutResource(R.layout.process_preference_category);
255 processPref.setTitle(entry.mLabel);
257 long duration = Math.max(entry.mRunDuration, entry.mBgDuration);
258 long memoryUse = Math.max((long) (entry.mRunWeight * mWeightToRam),
259 (long) (entry.mBgWeight * mWeightToRam));
260 String memoryString = Formatter.formatShortFileSize(getActivity(), memoryUse);
261 CharSequence frequency = ProcStatsPackageEntry.getFrequency(duration
262 / (float)mTotalTime, getActivity());
263 processPref.setSummary(
264 getString(R.string.memory_use_running_format, memoryString, frequency));
265 getPreferenceScreen().addPreference(processPref);
266 fillServicesSection(entry, processPref);
270 private static String capitalize(String processName) {
271 char c = processName.charAt(0);
272 if (!Character.isLowerCase(c)) {
275 return Character.toUpperCase(c) + processName.substring(1);
278 private static String getProcessName(String appLabel, ProcStatsEntry entry) {
279 String processName = entry.mName;
280 if (processName.contains(":")) {
281 return capitalize(processName.substring(processName.lastIndexOf(':') + 1));
283 if (processName.startsWith(entry.mPackage)) {
284 if (processName.length() == entry.mPackage.length()) {
287 int start = entry.mPackage.length();
288 if (processName.charAt(start) == '.') {
291 return capitalize(processName.substring(start));
296 final static Comparator<ProcStatsEntry.Service> sServiceCompare
297 = new Comparator<ProcStatsEntry.Service>() {
299 public int compare(ProcStatsEntry.Service lhs, ProcStatsEntry.Service rhs) {
300 if (lhs.mDuration < rhs.mDuration) {
302 } else if (lhs.mDuration > rhs.mDuration) {
309 final static Comparator<PkgService> sServicePkgCompare = new Comparator<PkgService>() {
311 public int compare(PkgService lhs, PkgService rhs) {
312 if (lhs.mDuration < rhs.mDuration) {
314 } else if (lhs.mDuration > rhs.mDuration) {
321 static class PkgService {
322 final ArrayList<ProcStatsEntry.Service> mServices = new ArrayList<>();
326 private void fillServicesSection(ProcStatsEntry entry, PreferenceCategory processPref) {
327 final HashMap<String, PkgService> pkgServices = new HashMap<>();
328 final ArrayList<PkgService> pkgList = new ArrayList<>();
329 for (int ip = 0; ip < entry.mServices.size(); ip++) {
330 String pkg = entry.mServices.keyAt(ip);
331 PkgService psvc = null;
332 ArrayList<ProcStatsEntry.Service> services = entry.mServices.valueAt(ip);
333 for (int is=services.size()-1; is>=0; is--) {
334 ProcStatsEntry.Service pent = services.get(is);
335 if (pent.mDuration >= mOnePercentTime) {
337 psvc = pkgServices.get(pkg);
339 psvc = new PkgService();
340 pkgServices.put(pkg, psvc);
344 psvc.mServices.add(pent);
345 psvc.mDuration += pent.mDuration;
349 Collections.sort(pkgList, sServicePkgCompare);
350 for (int ip = 0; ip < pkgList.size(); ip++) {
351 ArrayList<ProcStatsEntry.Service> services = pkgList.get(ip).mServices;
352 Collections.sort(services, sServiceCompare);
353 for (int is=0; is<services.size(); is++) {
354 final ProcStatsEntry.Service service = services.get(is);
355 CharSequence label = getLabel(service);
356 CancellablePreference servicePref = new CancellablePreference(getActivity());
357 servicePref.setSelectable(false);
358 servicePref.setTitle(label);
359 servicePref.setSummary(ProcStatsPackageEntry.getFrequency(
360 service.mDuration / (float) mTotalTime, getActivity()));
361 processPref.addPreference(servicePref);
362 mServiceMap.put(new ComponentName(service.mPackage, service.mName), servicePref);
367 private CharSequence getLabel(Service service) {
368 // Try to get the service label, on the off chance that one exists.
370 ServiceInfo serviceInfo = getPackageManager().getServiceInfo(
371 new ComponentName(service.mPackage, service.mName), 0);
372 if (serviceInfo.labelRes != 0) {
373 return serviceInfo.loadLabel(getPackageManager());
375 } catch (NameNotFoundException e) {
377 String label = service.mName;
378 int tail = label.lastIndexOf('.');
379 if (tail >= 0 && tail < (label.length()-1)) {
380 label = label.substring(tail+1);
385 private void stopService(String pkg, String name) {
387 ApplicationInfo appInfo = getActivity().getPackageManager().getApplicationInfo(pkg, 0);
388 if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
389 showStopServiceDialog(pkg, name);
392 } catch (NameNotFoundException e) {
393 Log.e(TAG, "Can't find app " + pkg, e);
396 doStopService(pkg, name);
399 private void showStopServiceDialog(final String pkg, final String name) {
400 new AlertDialog.Builder(getActivity())
401 .setTitle(R.string.runningservicedetails_stop_dlg_title)
402 .setMessage(R.string.runningservicedetails_stop_dlg_text)
403 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
404 public void onClick(DialogInterface dialog, int which) {
405 doStopService(pkg, name);
408 .setNegativeButton(R.string.dlg_cancel, null)
412 private void doStopService(String pkg, String name) {
413 getActivity().stopService(new Intent().setClassName(pkg, name));
414 updateRunningServices();
417 private void killProcesses() {
418 ActivityManager am = (ActivityManager)getActivity().getSystemService(
419 Context.ACTIVITY_SERVICE);
420 for (int i=0; i< mApp.mEntries.size(); i++) {
421 ProcStatsEntry ent = mApp.mEntries.get(i);
422 for (int j=0; j<ent.mPackages.size(); j++) {
423 am.forceStopPackage(ent.mPackages.get(j));
429 private void checkForceStop() {
430 if (mApp.mEntries.get(0).mUid < Process.FIRST_APPLICATION_UID) {
431 mForceStopButton.setEnabled(false);
434 boolean isStarted = false;
435 for (int i=0; i< mApp.mEntries.size(); i++) {
436 ProcStatsEntry ent = mApp.mEntries.get(i);
437 for (int j=0; j<ent.mPackages.size(); j++) {
438 String pkg = ent.mPackages.get(j);
439 if (mDpm.packageHasActiveAdmins(pkg)) {
440 mForceStopButton.setEnabled(false);
444 ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
445 if ((info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
448 } catch (PackageManager.NameNotFoundException e) {
453 mForceStopButton.setEnabled(true);