/* * 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.os.UserHandle; 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_USE_USS = "use_uss"; 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 boolean mUseUss; 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.round(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); mUseUss = args.getBoolean(EXTRA_USE_USS); 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 addPackageHeaderItem(ViewGroup parent, String packageName) { LayoutInflater inflater = getActivity().getLayoutInflater(); ViewGroup item = (ViewGroup) inflater.inflate(R.layout.running_processes_item, null); parent.addView(item); final ImageView icon = (ImageView) item.findViewById(R.id.icon); TextView nameView = (TextView) item.findViewById(R.id.name); TextView descriptionView = (TextView) item.findViewById(R.id.description); try { ApplicationInfo ai = mPm.getApplicationInfo(packageName, 0); icon.setImageDrawable(ai.loadIcon(mPm)); nameView.setText(ai.loadLabel(mPm)); } catch (PackageManager.NameNotFoundException e) { } descriptionView.setText(packageName); } 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_avg_ram_use), Formatter.formatShortFileSize(getActivity(), (mUseUss ? mEntry.mAvgUss : mEntry.mAvgPss) * 1024)); addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_max_ram_use), Formatter.formatShortFileSize(getActivity(), (mUseUss ? mEntry.mMaxUss : mEntry.mMaxPss) * 1024)); addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_run_time), makePercentString(getResources(), mEntry.mDuration, mTotalTime)); } final static Comparator sServiceCompare = new Comparator() { @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; } }; final static Comparator> sServicePkgCompare = new Comparator>() { @Override public int compare(ArrayList lhs, ArrayList rhs) { long topLhs = lhs.size() > 0 ? lhs.get(0).mDuration : 0; long topRhs = rhs.size() > 0 ? rhs.get(0).mDuration : 0; if (topLhs < topRhs) { return 1; } else if (topLhs > topRhs) { return -1; } return 0; } }; private void fillServicesSection() { if (mEntry.mServices.size() > 0) { boolean addPackageSections = false; // Sort it all. ArrayList> servicePkgs = new ArrayList>(); for (int ip=0; ip services = (ArrayList)mEntry.mServices.valueAt(ip).clone(); Collections.sort(services, sServiceCompare); servicePkgs.add(services); } if (mEntry.mServices.size() > 1 || !mEntry.mServices.valueAt(0).get(0).mPackage.equals(mEntry.mPackage)) { addPackageSections = true; // Sort these so that the one(s) with the longest run durations are on top. Collections.sort(servicePkgs, sServicePkgCompare); } for (int ip=0; ip services = servicePkgs.get(ip); if (addPackageSections) { addPackageHeaderItem(mServicesParent, services.get(0).mPackage); } for (int is=0; is= 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, UserHandle.getUserId(mEntry.mUid)); getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); } }