Loading res/layout/process_stats_details.xml 0 → 100644 +76 −0 Original line number Diff line number Diff line <?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> res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -3614,6 +3614,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 --> Loading src/com/android/settings/applications/ProcStatsEntry.java +203 −11 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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; Loading @@ -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]; } }; } } src/com/android/settings/applications/ProcessStatsDetail.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * 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); } } src/com/android/settings/applications/ProcessStatsPreference.java +7 −1 Original line number Diff line number Diff line Loading @@ -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( Loading Loading
res/layout/process_stats_details.xml 0 → 100644 +76 −0 Original line number Diff line number Diff line <?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>
res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -3614,6 +3614,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 --> Loading
src/com/android/settings/applications/ProcStatsEntry.java +203 −11 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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; Loading @@ -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]; } }; } }
src/com/android/settings/applications/ProcessStatsDetail.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * 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); } }
src/com/android/settings/applications/ProcessStatsPreference.java +7 −1 Original line number Diff line number Diff line Loading @@ -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( Loading