Add details screen to proc stats.

Lets you see the actual RAM use and % running time, a list of
all of the services that are associated with it (kind-of, not
working correctly for those that share processes), and most
importantly a glorious FORCE STOP button.

Change-Id: I34ac30c88f1187227a8a7809ae103118c8b9a865
This commit is contained in:
Dianne Hackborn
2013-09-04 15:47:59 -07:00
parent 35ec3b7fa3
commit 0448673834
6 changed files with 566 additions and 116 deletions

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbarStyle="@integer/preference_scrollbar_style">
<LinearLayout
android:id="@+id/all_details"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:orientation="vertical">
<include layout="@layout/app_percentage_item" />
<!-- Force stop and report buttons -->
<LinearLayout
android:id="@+id/two_buttons_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="6dip"
android:orientation="vertical">
<include
layout="@layout/two_buttons_panel"/>
</LinearLayout>
<TextView
style="?android:attr/listSeparatorTextViewStyle"
android:text="@string/details_subtitle" />
<LinearLayout
android:id="@+id/details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="6dip"
android:orientation="vertical">
<!-- Insert detail items here -->
</LinearLayout>
<TextView
style="?android:attr/listSeparatorTextViewStyle"
android:text="@string/services_subtitle" />
<LinearLayout
android:id="@+id/services"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="6dip"
android:orientation="vertical">
<!-- Insert service items here -->
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -3604,6 +3604,12 @@
<!-- [CHAR LIMIT=NONE] Label for process stats, duration of time the stats are over -->
<string name="process_stats_memory_status">Device memory is currently
<xliff:g id="memstate">%1$s</xliff:g></string>
<!-- [CHAR LIMIT=NONE] Label for item showing details of average RAM use -->
<string name="process_stats_ram_use">Average RAM use</string>
<!-- [CHAR LIMIT=NONE] Label for item showing percent of time spent running -->
<string name="process_stats_run_time">Run time</string>
<!-- [CHAR LIMIT=NONE] Subtitle for process stats services list -->
<string name="services_subtitle">Services</string>
<!-- Voice input/output settings --><skip />
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to speech functionality -->

View File

@@ -16,11 +16,22 @@
package com.android.settings.applications;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.app.ProcessStats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public final class ProcStatsEntry implements Parcelable {
private static final String TAG = "ProcStatsEntry";
public final class ProcStatsEntry {
final String mPackage;
final int mUid;
final String mName;
@@ -29,7 +40,14 @@ public final class ProcStatsEntry {
final long mAvgPss;
final long mWeight;
ArrayList<Service> mServices;
String mBestTargetPackage;
ArrayList<Service> mServices = new ArrayList<Service>(2);
public ApplicationInfo mUiTargetApp;
public String mUiLabel;
public String mUiBaseLabel;
public String mUiPackage;
public ProcStatsEntry(ProcessStats.ProcessState proc,
ProcessStats.ProcessDataCollection tmpTotals) {
@@ -43,18 +61,156 @@ public final class ProcStatsEntry {
mWeight = mDuration * mAvgPss;
}
public ProcStatsEntry(Parcel in) {
mPackage = in.readString();
mUid = in.readInt();
mName = in.readString();
mUnique = in.readInt() != 0;
mDuration = in.readLong();
mAvgPss = in.readLong();
mWeight = in.readLong();
mBestTargetPackage = in.readString();
in.readTypedList(mServices, Service.CREATOR);
}
public void evaluateTargetPackage(ProcessStats stats,
ProcessStats.ProcessDataCollection totals, Comparator<ProcStatsEntry> compare) {
mBestTargetPackage = null;
if (mUnique) {
mBestTargetPackage = mPackage;
addServices(stats.getPackageStateLocked(mPackage, mUid));
} else {
// See if there is one significant package that was running here.
ArrayList<ProcStatsEntry> subProcs = new ArrayList<ProcStatsEntry>();
for (int ipkg=0, NPKG=stats.mPackages.getMap().size(); ipkg<NPKG; ipkg++) {
SparseArray<ProcessStats.PackageState> uids
= stats.mPackages.getMap().valueAt(ipkg);
for (int iu=0, NU=uids.size(); iu<NU; iu++) {
if (uids.keyAt(iu) != mUid) {
continue;
}
ProcessStats.PackageState pkgState = uids.valueAt(iu);
boolean match = false;
for (int iproc=0, NPROC=pkgState.mProcesses.size(); iproc<NPROC; iproc++) {
ProcessStats.ProcessState subProc =
pkgState.mProcesses.valueAt(iproc);
if (subProc.mName.equals(mName)) {
match = true;
subProcs.add(new ProcStatsEntry(subProc, totals));
}
}
if (match) {
addServices(stats.getPackageStateLocked(mPackage, mUid));
}
}
}
if (subProcs.size() > 1) {
Collections.sort(subProcs, compare);
if (subProcs.get(0).mWeight > (subProcs.get(1).mWeight*3)) {
mBestTargetPackage = subProcs.get(0).mPackage;
}
}
}
}
public void retrieveUiData(PackageManager pm) {
mUiTargetApp = null;
mUiLabel = mUiBaseLabel = mName;
mUiPackage = mBestTargetPackage;
if (mUiPackage != null) {
// Only one app associated with this process.
try {
mUiTargetApp = pm.getApplicationInfo(mUiPackage,
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES);
String name = mUiBaseLabel = mUiTargetApp.loadLabel(pm).toString();
if (mName.equals(mUiPackage)) {
mUiLabel = name;
} else {
if (mName.startsWith(mUiPackage)) {
int off = mUiPackage.length();
if (mName.length() > off) {
off++;
}
mUiLabel = name + " (" + mName.substring(off) + ")";
} else {
mUiLabel = name + " (" + mName + ")";
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
if (mUiTargetApp == null) {
String[] packages = pm.getPackagesForUid(mUid);
if (packages != null) {
for (String curPkg : packages) {
try {
final PackageInfo pi = pm.getPackageInfo(curPkg,
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES);
if (pi.sharedUserLabel != 0) {
mUiTargetApp = pi.applicationInfo;
final CharSequence nm = pm.getText(curPkg,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
mUiBaseLabel = nm.toString();
mUiLabel = mUiBaseLabel + " (" + mName + ")";
} else {
mUiBaseLabel = mUiTargetApp.loadLabel(pm).toString();
mUiLabel = mUiBaseLabel + " (" + mName + ")";
}
break;
}
} catch (PackageManager.NameNotFoundException e) {
}
}
} else {
// no current packages for this uid, typically because of uninstall
Log.i(TAG, "No package for uid " + mUid);
}
}
}
public void addServices(ProcessStats.PackageState pkgState) {
for (int isvc=0, NSVC=pkgState.mServices.size(); isvc<NSVC; isvc++) {
ProcessStats.ServiceState svc = pkgState.mServices.valueAt(isvc);
// XXX can't tell what process it is in!
if (mServices == null) {
mServices = new ArrayList<Service>();
}
mServices.add(new Service(svc));
}
}
public static final class Service {
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mPackage);
dest.writeInt(mUid);
dest.writeString(mName);
dest.writeInt(mUnique ? 1 : 0);
dest.writeLong(mDuration);
dest.writeLong(mAvgPss);
dest.writeLong(mWeight);
dest.writeString(mBestTargetPackage);
dest.writeTypedList(mServices);
}
public static final Parcelable.Creator<ProcStatsEntry> CREATOR
= new Parcelable.Creator<ProcStatsEntry>() {
public ProcStatsEntry createFromParcel(Parcel in) {
return new ProcStatsEntry(in);
}
public ProcStatsEntry[] newArray(int size) {
return new ProcStatsEntry[size];
}
};
public static final class Service implements Parcelable {
final String mPackage;
final String mName;
final long mDuration;
@@ -62,15 +218,51 @@ public final class ProcStatsEntry {
public Service(ProcessStats.ServiceState service) {
mPackage = service.mPackage;
mName = service.mName;
mDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
long startDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.ServiceState.SERVICE_STARTED,
ProcessStats.STATE_NOTHING, 0, 0)
+ ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.STATE_NOTHING, 0, 0);
long bindDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.ServiceState.SERVICE_BOUND,
ProcessStats.STATE_NOTHING, 0, 0)
+ ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.STATE_NOTHING, 0, 0);
long execDuration = ProcessStats.dumpSingleServiceTime(null, null, service,
ProcessStats.ServiceState.SERVICE_EXEC,
ProcessStats.STATE_NOTHING, 0, 0);
if (bindDuration > startDuration) {
startDuration = bindDuration;
}
if (execDuration > startDuration) {
startDuration = execDuration;
}
mDuration = startDuration;
}
public Service(Parcel in) {
mPackage = in.readString();
mName = in.readString();
mDuration = in.readLong();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mPackage);
dest.writeString(mName);
dest.writeLong(mDuration);
}
public static final Parcelable.Creator<Service> CREATOR
= new Parcelable.Creator<Service>() {
public Service createFromParcel(Parcel in) {
return new Service(in);
}
public Service[] newArray(int size) {
return new Service[size];
}
};
}
}

View File

@@ -0,0 +1,258 @@
/*
* 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.app.Activity;
import android.app.ActivityManager;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
import android.preference.PreferenceActivity;
import android.text.format.Formatter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import static com.android.settings.Utils.prepareCustomPreferencesList;
public class ProcessStatsDetail extends Fragment implements Button.OnClickListener {
private static final String TAG = "ProcessStatsDetail";
public static final int ACTION_FORCE_STOP = 1;
public static final String EXTRA_ENTRY = "entry";
public static final String EXTRA_MAX_WEIGHT = "max_weight";
public static final String EXTRA_TOTAL_TIME = "total_time";
private PackageManager mPm;
private DevicePolicyManager mDpm;
private ProcStatsEntry mEntry;
private long mMaxWeight;
private long mTotalTime;
private View mRootView;
private TextView mTitleView;
private ViewGroup mTwoButtonsPanel;
private Button mForceStopButton;
private Button mReportButton;
private ViewGroup mDetailsParent;
private ViewGroup mServicesParent;
public static String makePercentString(Resources res, long amount, long total) {
final double percent = (((double)amount) / total) * 100;
return res.getString(R.string.percentage, (int) Math.ceil(percent));
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getActivity().getPackageManager();
mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
final Bundle args = getArguments();
mEntry = (ProcStatsEntry)args.getParcelable(EXTRA_ENTRY);
mEntry.retrieveUiData(mPm);
mMaxWeight = args.getLong(EXTRA_MAX_WEIGHT);
mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.process_stats_details, container, false);
prepareCustomPreferencesList(container, view, view, false);
mRootView = view;
createDetails();
return view;
}
@Override
public void onResume() {
super.onResume();
checkForceStop();
}
@Override
public void onPause() {
super.onPause();
}
private void createDetails() {
final double percentOfWeight = (((double)mEntry.mWeight) / mMaxWeight) * 100;
int appLevel = (int) Math.ceil(percentOfWeight);
String appLevelText = makePercentString(getResources(), mEntry.mDuration, mTotalTime);
// Set all values in the header.
final TextView summary = (TextView) mRootView.findViewById(android.R.id.summary);
summary.setText(mEntry.mName);
summary.setVisibility(View.VISIBLE);
mTitleView = (TextView) mRootView.findViewById(android.R.id.title);
mTitleView.setText(mEntry.mUiBaseLabel);
final TextView text1 = (TextView)mRootView.findViewById(android.R.id.text1);
text1.setText(appLevelText);
final ProgressBar progress = (ProgressBar) mRootView.findViewById(android.R.id.progress);
progress.setProgress(appLevel);
final ImageView icon = (ImageView) mRootView.findViewById(android.R.id.icon);
if (mEntry.mUiTargetApp != null) {
icon.setImageDrawable(mEntry.mUiTargetApp.loadIcon(mPm));
}
mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel);
mForceStopButton = (Button)mRootView.findViewById(R.id.right_button);
mReportButton = (Button)mRootView.findViewById(R.id.left_button);
mForceStopButton.setEnabled(false);
mReportButton.setVisibility(View.INVISIBLE);
mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
mServicesParent = (ViewGroup)mRootView.findViewById(R.id.services);
fillDetailsSection();
fillServicesSection();
if (mEntry.mUid >= android.os.Process.FIRST_APPLICATION_UID) {
mForceStopButton.setText(R.string.force_stop);
mForceStopButton.setTag(ACTION_FORCE_STOP);
mForceStopButton.setOnClickListener(this);
mTwoButtonsPanel.setVisibility(View.VISIBLE);
} else {
mTwoButtonsPanel.setVisibility(View.GONE);
}
}
public void onClick(View v) {
doAction((Integer) v.getTag());
}
private void doAction(int action) {
PreferenceActivity pa = (PreferenceActivity)getActivity();
switch (action) {
case ACTION_FORCE_STOP:
killProcesses();
break;
}
}
private void addDetailsItem(ViewGroup parent, CharSequence label, CharSequence value) {
LayoutInflater inflater = getActivity().getLayoutInflater();
ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
null);
parent.addView(item);
TextView labelView = (TextView) item.findViewById(R.id.label);
TextView valueView = (TextView) item.findViewById(R.id.value);
labelView.setText(label);
valueView.setText(value);
}
private void fillDetailsSection() {
addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_ram_use),
Formatter.formatShortFileSize(getActivity(), mEntry.mAvgPss * 1024));
addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_run_time),
makePercentString(getResources(), mEntry.mDuration, mTotalTime));
}
final static Comparator<ProcStatsEntry.Service> sServiceCompare
= new Comparator<ProcStatsEntry.Service>() {
@Override
public int compare(ProcStatsEntry.Service lhs, ProcStatsEntry.Service rhs) {
if (lhs.mDuration < rhs.mDuration) {
return 1;
} else if (lhs.mDuration > rhs.mDuration) {
return -1;
}
return 0;
}
};
private void fillServicesSection() {
LayoutInflater inflater = getActivity().getLayoutInflater();
if (mEntry.mServices.size() > 0) {
ArrayList<ProcStatsEntry.Service> services =
(ArrayList<ProcStatsEntry.Service>)mEntry.mServices.clone();
Collections.sort(services, sServiceCompare);
for (int i=0; i<services.size(); i++) {
ProcStatsEntry.Service service = services.get(i);
String label = service.mName;
int tail = label.lastIndexOf('.');
if (tail >= 0 && tail < (label.length()-1)) {
label = label.substring(tail+1);
}
long duration = service.mDuration;
final double percentOfTime = (((double)duration) / mTotalTime) * 100;
addDetailsItem(mServicesParent, label, getActivity().getResources().getString(
R.string.percentage, (int) Math.ceil(percentOfTime)));
}
}
}
private void killProcesses() {
ActivityManager am = (ActivityManager)getActivity().getSystemService(
Context.ACTIVITY_SERVICE);
am.forceStopPackage(mEntry.mUiPackage);
checkForceStop();
}
private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
}
};
private void checkForceStop() {
if (mEntry.mUiPackage == null || mEntry.mUid < Process.FIRST_APPLICATION_UID) {
mForceStopButton.setEnabled(false);
return;
}
if (mDpm.packageHasActiveAdmins(mEntry.mUiPackage)) {
mForceStopButton.setEnabled(false);
return;
}
try {
ApplicationInfo info = mPm.getApplicationInfo(mEntry.mUiPackage, 0);
if ((info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
mForceStopButton.setEnabled(true);
}
} catch (PackageManager.NameNotFoundException e) {
}
Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
Uri.fromParts("package", mEntry.mUiPackage, null));
intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mEntry.mUiPackage });
intent.putExtra(Intent.EXTRA_UID, mEntry.mUid);
intent.putExtra(Intent.EXTRA_USER_HANDLE, mEntry.mUid);
getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
Activity.RESULT_CANCELED, null, null);
}
}

View File

@@ -27,15 +27,21 @@ import android.widget.TextView;
import com.android.settings.R;
public class ProcessStatsPreference extends Preference {
private final ProcStatsEntry mEntry;
private int mProgress;
private CharSequence mProgressText;
public ProcessStatsPreference(Context context, Drawable icon) {
public ProcessStatsPreference(Context context, Drawable icon, ProcStatsEntry entry) {
super(context);
mEntry = entry;
setLayoutResource(R.layout.app_percentage_item);
setIcon(icon != null ? icon : new ColorDrawable(0));
}
public ProcStatsEntry getEntry() {
return mEntry;
}
public void setPercent(double percentOfWeight, double percentOfTime) {
mProgress = (int) Math.ceil(percentOfWeight);
mProgressText = getContext().getResources().getString(

View File

@@ -17,8 +17,6 @@
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;
@@ -28,10 +26,10 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserManager;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -83,6 +81,7 @@ public class ProcessStatsUi extends PreferenceFragment {
private PreferenceGroup mAppListGroup;
private Preference mMemStatusPref;
long mMaxWeight;
long mTotalTime;
@Override
@@ -127,11 +126,13 @@ public class ProcessStatsUi extends PreferenceFragment {
return false;
}
/*
PreferenceActivity pa = (PreferenceActivity)getActivity();
pa.startPreferencePanel(PowerUsageDetail.class.getName(), args,
R.string.details_title, null, null, 0);
*/
ProcessStatsPreference pgp = (ProcessStatsPreference) preference;
Bundle args = new Bundle();
args.putParcelable(ProcessStatsDetail.EXTRA_ENTRY, pgp.getEntry());
args.putLong(ProcessStatsDetail.EXTRA_MAX_WEIGHT, mMaxWeight);
args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, mTotalTime);
((PreferenceActivity) getActivity()).startPreferencePanel(
ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0);
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
@@ -263,108 +264,19 @@ public class ProcessStatsUi extends PreferenceFragment {
maxWeight = proc.mWeight;
}
}
mMaxWeight = maxWeight;
for (int i=0, N=(procs != null ? procs.size() : 0); i<N; i++) {
ProcStatsEntry proc = procs.get(i);
final double percentOfWeight = (((double)proc.mWeight) / maxWeight) * 100;
final double percentOfTime = (((double)proc.mDuration) / mTotalTime) * 100;
if (percentOfWeight < 1) continue;
ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null);
ApplicationInfo targetApp = null;
String label = proc.mName;
String pkgName = null;
if (proc.mUnique) {
pkgName = proc.mPackage;
proc.addServices(mStats.getPackageStateLocked(proc.mPackage, proc.mUid));
} else {
// See if there is one significant package that was running here.
ArrayList<ProcStatsEntry> subProcs = new ArrayList<ProcStatsEntry>();
for (int ipkg=0, NPKG=mStats.mPackages.getMap().size(); ipkg<NPKG; ipkg++) {
SparseArray<ProcessStats.PackageState> uids
= mStats.mPackages.getMap().valueAt(ipkg);
for (int iu=0, NU=uids.size(); iu<NU; iu++) {
if (uids.keyAt(iu) != proc.mUid) {
continue;
}
ProcessStats.PackageState pkgState = uids.valueAt(iu);
boolean match = false;
for (int iproc=0, NPROC=pkgState.mProcesses.size(); iproc<NPROC; iproc++) {
ProcessStats.ProcessState subProc =
pkgState.mProcesses.valueAt(iproc);
if (subProc.mName.equals(proc.mName)) {
match = true;
subProcs.add(new ProcStatsEntry(subProc, totals));
}
}
if (match) {
proc.addServices(mStats.getPackageStateLocked(proc.mPackage,
proc.mUid));
}
}
}
if ( subProcs.size() > 1) {
Collections.sort(subProcs, sEntryCompare);
if (subProcs.get(0).mWeight > (subProcs.get(1).mWeight*3)) {
pkgName = subProcs.get(0).mPackage;
}
}
}
if (pkgName != null) {
// Only one app associated with this process.
try {
targetApp = pm.getApplicationInfo(pkgName,
PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.GET_UNINSTALLED_PACKAGES);
String name = targetApp.loadLabel(pm).toString();
if (proc.mName.equals(pkgName)) {
label = name;
} else {
if (proc.mName.startsWith(pkgName)) {
int off = pkgName.length();
if (proc.mName.length() > off) {
off++;
}
label = name + " (" + proc.mName.substring(off) + ")";
} else {
label = name + " (" + proc.mName + ")";
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
if (targetApp == null) {
String[] packages = pm.getPackagesForUid(proc.mUid);
if (packages != null) {
for (String curPkg : packages) {
try {
final PackageInfo pi = pm.getPackageInfo(curPkg,
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(curPkg,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
label = nm.toString() + " (" + proc.mName + ")";
} else {
label = targetApp.loadLabel(pm).toString() + " ("
+ proc.mName + ")";
}
break;
}
} catch (PackageManager.NameNotFoundException e) {
}
}
} else {
// no current packages for this uid, typically because of uninstall
Log.i(TAG, "No package for uid " + proc.mUid);
}
}
pref.setTitle(label);
if (targetApp != null) {
pref.setIcon(targetApp.loadIcon(pm));
ProcessStatsPreference pref = new ProcessStatsPreference(getActivity(), null, proc);
proc.evaluateTargetPackage(mStats, totals, sEntryCompare);
proc.retrieveUiData(pm);
pref.setTitle(proc.mUiLabel);
if (proc.mUiTargetApp != null) {
pref.setIcon(proc.mUiTargetApp.loadIcon(pm));
}
pref.setOrder(i);
pref.setPercent(percentOfWeight, percentOfTime);