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.Activity;
20 import android.app.ActivityManager;
21 import android.app.ActivityManager.RunningServiceInfo;
22 import android.app.AlertDialog;
23 import android.app.admin.DevicePolicyManager;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.Intent;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageManager.NameNotFoundException;
31 import android.content.pm.ServiceInfo;
32 import android.graphics.drawable.ColorDrawable;
33 import android.os.Bundle;
34 import android.os.Process;
35 import android.os.UserHandle;
36 import android.support.v7.preference.Preference;
37 import android.support.v7.preference.PreferenceCategory;
38 import android.text.format.Formatter;
39 import android.util.ArrayMap;
40 import android.util.IconDrawableFactory;
41 import android.util.Log;
42 import android.view.Menu;
43 import android.view.MenuInflater;
44 import android.view.MenuItem;
45 import android.view.View;
47 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
48 import com.android.settings.CancellablePreference;
49 import com.android.settings.CancellablePreference.OnCancelListener;
50 import com.android.settings.R;
51 import com.android.settings.SettingsPreferenceFragment;
52 import com.android.settings.SummaryPreference;
53 import com.android.settings.applications.ProcStatsEntry.Service;
54 import com.android.settings.widget.EntityHeaderController;
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.Comparator;
59 import java.util.HashMap;
60 import java.util.List;
62 import static com.android.settings.widget.EntityHeaderController.ActionType;
64 public class ProcessStatsDetail extends SettingsPreferenceFragment {
66 private static final String TAG = "ProcessStatsDetail";
68 public static final int MENU_FORCE_STOP = 1;
70 public static final String EXTRA_PACKAGE_ENTRY = "package_entry";
71 public static final String EXTRA_WEIGHT_TO_RAM = "weight_to_ram";
72 public static final String EXTRA_TOTAL_TIME = "total_time";
73 public static final String EXTRA_MAX_MEMORY_USAGE = "max_memory_usage";
74 public static final String EXTRA_TOTAL_SCALE = "total_scale";
76 private static final String KEY_DETAILS_HEADER = "status_header";
78 private static final String KEY_FREQUENCY = "frequency";
79 private static final String KEY_MAX_USAGE = "max_usage";
81 private static final String KEY_PROCS = "processes";
83 private final ArrayMap<ComponentName, CancellablePreference> mServiceMap = new ArrayMap<>();
85 private PackageManager mPm;
86 private DevicePolicyManager mDpm;
88 private MenuItem mForceStop;
90 private ProcStatsPackageEntry mApp;
91 private double mWeightToRam;
92 private long mTotalTime;
93 private long mOnePercentTime;
95 private double mMaxMemoryUsage;
97 private double mTotalScale;
99 private PreferenceCategory mProcGroup;
102 public void onCreate(Bundle icicle) {
103 super.onCreate(icicle);
104 mPm = getActivity().getPackageManager();
105 mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
106 final Bundle args = getArguments();
107 mApp = args.getParcelable(EXTRA_PACKAGE_ENTRY);
108 mApp.retrieveUiData(getActivity(), mPm);
109 mWeightToRam = args.getDouble(EXTRA_WEIGHT_TO_RAM);
110 mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
111 mMaxMemoryUsage = args.getDouble(EXTRA_MAX_MEMORY_USAGE);
112 mTotalScale = args.getDouble(EXTRA_TOTAL_SCALE);
113 mOnePercentTime = mTotalTime/100;
117 setHasOptionsMenu(true);
121 public void onViewCreated(View view, Bundle savedInstanceState) {
122 super.onViewCreated(view, savedInstanceState);
124 if (mApp.mUiTargetApp == null) {
128 final Activity activity = getActivity();
129 final Preference pref = EntityHeaderController
130 .newInstance(activity, this, null /* appHeader */)
131 .setRecyclerView(getListView(), getLifecycle())
132 .setIcon(mApp.mUiTargetApp != null
133 ? IconDrawableFactory.newInstance(activity).getBadgedIcon(mApp.mUiTargetApp)
134 : new ColorDrawable(0))
135 .setLabel(mApp.mUiLabel)
136 .setPackageName(mApp.mPackage)
137 .setUid(mApp.mUiTargetApp != null
138 ? mApp.mUiTargetApp.uid
139 : UserHandle.USER_NULL)
140 .setHasAppInfoLink(true)
141 .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
142 .done(activity, getPrefContext());
143 getPreferenceScreen().addPreference(pref);
147 public int getMetricsCategory() {
148 return MetricsEvent.APPLICATIONS_PROCESS_STATS_DETAIL;
152 public void onResume() {
156 updateRunningServices();
159 private void updateRunningServices() {
160 ActivityManager activityManager = (ActivityManager)
161 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
162 List<RunningServiceInfo> runningServices =
163 activityManager.getRunningServices(Integer.MAX_VALUE);
165 // Set all services as not running, then turn back on the ones we find.
166 int N = mServiceMap.size();
167 for (int i = 0; i < N; i++) {
168 mServiceMap.valueAt(i).setCancellable(false);
171 N = runningServices.size();
172 for (int i = 0; i < N; i++) {
173 RunningServiceInfo runningService = runningServices.get(i);
174 if (!runningService.started && runningService.clientLabel == 0) {
177 if ((runningService.flags & RunningServiceInfo.FLAG_PERSISTENT_PROCESS) != 0) {
180 final ComponentName service = runningService.service;
181 CancellablePreference pref = mServiceMap.get(service);
183 pref.setOnCancelListener(new OnCancelListener() {
185 public void onCancel(CancellablePreference preference) {
186 stopService(service.getPackageName(), service.getClassName());
189 pref.setCancellable(true);
194 private void createDetails() {
195 addPreferencesFromResource(R.xml.app_memory_settings);
197 mProcGroup = (PreferenceCategory) findPreference(KEY_PROCS);
198 fillProcessesSection();
200 SummaryPreference summaryPreference = (SummaryPreference) findPreference(KEY_DETAILS_HEADER);
202 // TODO: Find way to share this code with ProcessStatsPreference.
203 boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight;
204 double avgRam = (statsForeground ? mApp.mRunWeight : mApp.mBgWeight) * mWeightToRam;
205 float avgRatio = (float) (avgRam / mMaxMemoryUsage);
206 float remainingRatio = 1 - avgRatio;
207 Context context = getActivity();
208 summaryPreference.setRatios(avgRatio, 0, remainingRatio);
209 Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
210 (long) avgRam, Formatter.FLAG_SHORTER);
211 summaryPreference.setAmount(usedResult.value);
212 summaryPreference.setUnits(usedResult.units);
214 long duration = Math.max(mApp.mRunDuration, mApp.mBgDuration);
215 CharSequence frequency = ProcStatsPackageEntry.getFrequency(duration
216 / (float) mTotalTime, getActivity());
217 findPreference(KEY_FREQUENCY).setSummary(frequency);
218 double max = Math.max(mApp.mMaxBgMem, mApp.mMaxRunMem) * mTotalScale * 1024;
219 findPreference(KEY_MAX_USAGE).setSummary(
220 Formatter.formatShortFileSize(getContext(), (long) max));
224 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
225 mForceStop = menu.add(0, MENU_FORCE_STOP, 0, R.string.force_stop);
230 public boolean onOptionsItemSelected(MenuItem item) {
231 switch (item.getItemId()) {
232 case MENU_FORCE_STOP:
239 final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
241 public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
242 if (lhs.mRunWeight < rhs.mRunWeight) {
244 } else if (lhs.mRunWeight > rhs.mRunWeight) {
251 private void fillProcessesSection() {
252 mProcGroup.removeAll();
253 final ArrayList<ProcStatsEntry> entries = new ArrayList<>();
254 for (int ie = 0; ie < mApp.mEntries.size(); ie++) {
255 ProcStatsEntry entry = mApp.mEntries.get(ie);
256 if (entry.mPackage.equals("os")) {
257 entry.mLabel = entry.mName;
259 entry.mLabel = getProcessName(mApp.mUiLabel, entry);
263 Collections.sort(entries, sEntryCompare);
264 for (int ie = 0; ie < entries.size(); ie++) {
265 ProcStatsEntry entry = entries.get(ie);
266 Preference processPref = new Preference(getPrefContext());
267 processPref.setTitle(entry.mLabel);
268 processPref.setSelectable(false);
270 long duration = Math.max(entry.mRunDuration, entry.mBgDuration);
271 long memoryUse = Math.max((long) (entry.mRunWeight * mWeightToRam),
272 (long) (entry.mBgWeight * mWeightToRam));
273 String memoryString = Formatter.formatShortFileSize(getActivity(), memoryUse);
274 CharSequence frequency = ProcStatsPackageEntry.getFrequency(duration
275 / (float) mTotalTime, getActivity());
276 processPref.setSummary(
277 getString(R.string.memory_use_running_format, memoryString, frequency));
278 mProcGroup.addPreference(processPref);
280 if (mProcGroup.getPreferenceCount() < 2) {
281 getPreferenceScreen().removePreference(mProcGroup);
285 private static String capitalize(String processName) {
286 char c = processName.charAt(0);
287 if (!Character.isLowerCase(c)) {
290 return Character.toUpperCase(c) + processName.substring(1);
293 private static String getProcessName(String appLabel, ProcStatsEntry entry) {
294 String processName = entry.mName;
295 if (processName.contains(":")) {
296 return capitalize(processName.substring(processName.lastIndexOf(':') + 1));
298 if (processName.startsWith(entry.mPackage)) {
299 if (processName.length() == entry.mPackage.length()) {
302 int start = entry.mPackage.length();
303 if (processName.charAt(start) == '.') {
306 return capitalize(processName.substring(start));
311 final static Comparator<ProcStatsEntry.Service> sServiceCompare
312 = new Comparator<ProcStatsEntry.Service>() {
314 public int compare(ProcStatsEntry.Service lhs, ProcStatsEntry.Service rhs) {
315 if (lhs.mDuration < rhs.mDuration) {
317 } else if (lhs.mDuration > rhs.mDuration) {
324 final static Comparator<PkgService> sServicePkgCompare = new Comparator<PkgService>() {
326 public int compare(PkgService lhs, PkgService rhs) {
327 if (lhs.mDuration < rhs.mDuration) {
329 } else if (lhs.mDuration > rhs.mDuration) {
336 static class PkgService {
337 final ArrayList<ProcStatsEntry.Service> mServices = new ArrayList<>();
341 private void fillServicesSection(ProcStatsEntry entry, PreferenceCategory processPref) {
342 final HashMap<String, PkgService> pkgServices = new HashMap<>();
343 final ArrayList<PkgService> pkgList = new ArrayList<>();
344 for (int ip = 0; ip < entry.mServices.size(); ip++) {
345 String pkg = entry.mServices.keyAt(ip);
346 PkgService psvc = null;
347 ArrayList<ProcStatsEntry.Service> services = entry.mServices.valueAt(ip);
348 for (int is=services.size()-1; is>=0; is--) {
349 ProcStatsEntry.Service pent = services.get(is);
350 if (pent.mDuration >= mOnePercentTime) {
352 psvc = pkgServices.get(pkg);
354 psvc = new PkgService();
355 pkgServices.put(pkg, psvc);
359 psvc.mServices.add(pent);
360 psvc.mDuration += pent.mDuration;
364 Collections.sort(pkgList, sServicePkgCompare);
365 for (int ip = 0; ip < pkgList.size(); ip++) {
366 ArrayList<ProcStatsEntry.Service> services = pkgList.get(ip).mServices;
367 Collections.sort(services, sServiceCompare);
368 for (int is=0; is<services.size(); is++) {
369 final ProcStatsEntry.Service service = services.get(is);
370 CharSequence label = getLabel(service);
371 CancellablePreference servicePref = new CancellablePreference(getPrefContext());
372 servicePref.setSelectable(false);
373 servicePref.setTitle(label);
374 servicePref.setSummary(ProcStatsPackageEntry.getFrequency(
375 service.mDuration / (float) mTotalTime, getActivity()));
376 processPref.addPreference(servicePref);
377 mServiceMap.put(new ComponentName(service.mPackage, service.mName), servicePref);
382 private CharSequence getLabel(Service service) {
383 // Try to get the service label, on the off chance that one exists.
385 ServiceInfo serviceInfo = getPackageManager().getServiceInfo(
386 new ComponentName(service.mPackage, service.mName), 0);
387 if (serviceInfo.labelRes != 0) {
388 return serviceInfo.loadLabel(getPackageManager());
390 } catch (NameNotFoundException e) {
392 String label = service.mName;
393 int tail = label.lastIndexOf('.');
394 if (tail >= 0 && tail < (label.length()-1)) {
395 label = label.substring(tail+1);
400 private void stopService(String pkg, String name) {
402 ApplicationInfo appInfo = getActivity().getPackageManager().getApplicationInfo(pkg, 0);
403 if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
404 showStopServiceDialog(pkg, name);
407 } catch (NameNotFoundException e) {
408 Log.e(TAG, "Can't find app " + pkg, e);
411 doStopService(pkg, name);
414 private void showStopServiceDialog(final String pkg, final String name) {
415 new AlertDialog.Builder(getActivity())
416 .setTitle(R.string.runningservicedetails_stop_dlg_title)
417 .setMessage(R.string.runningservicedetails_stop_dlg_text)
418 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
419 public void onClick(DialogInterface dialog, int which) {
420 doStopService(pkg, name);
423 .setNegativeButton(R.string.dlg_cancel, null)
427 private void doStopService(String pkg, String name) {
428 getActivity().stopService(new Intent().setClassName(pkg, name));
429 updateRunningServices();
432 private void killProcesses() {
433 ActivityManager am = (ActivityManager)getActivity().getSystemService(
434 Context.ACTIVITY_SERVICE);
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 am.forceStopPackage(ent.mPackages.get(j));
443 private void checkForceStop() {
444 if (mForceStop == null) {
447 if (mApp.mEntries.get(0).mUid < Process.FIRST_APPLICATION_UID) {
448 mForceStop.setVisible(false);
451 boolean isStarted = false;
452 for (int i=0; i< mApp.mEntries.size(); i++) {
453 ProcStatsEntry ent = mApp.mEntries.get(i);
454 for (int j=0; j<ent.mPackages.size(); j++) {
455 String pkg = ent.mPackages.get(j);
456 if (mDpm.packageHasActiveAdmins(pkg)) {
457 mForceStop.setEnabled(false);
461 ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
462 if ((info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
465 } catch (PackageManager.NameNotFoundException e) {
470 mForceStop.setVisible(true);