Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      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
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     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.
     15  */
     16 
     17 package com.android.settings.applications;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManager;
     21 import android.app.Fragment;
     22 import android.app.admin.DevicePolicyManager;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.pm.ApplicationInfo;
     27 import android.content.pm.PackageManager;
     28 import android.content.res.Resources;
     29 import android.net.Uri;
     30 import android.os.Bundle;
     31 import android.os.Process;
     32 import android.os.UserHandle;
     33 import android.preference.PreferenceActivity;
     34 import android.text.format.Formatter;
     35 import android.view.LayoutInflater;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.widget.Button;
     39 import android.widget.ImageView;
     40 import android.widget.ProgressBar;
     41 import android.widget.TextView;
     42 import com.android.settings.R;
     43 
     44 import java.util.ArrayList;
     45 import java.util.Collections;
     46 import java.util.Comparator;
     47 
     48 import static com.android.settings.Utils.prepareCustomPreferencesList;
     49 
     50 public class ProcessStatsDetail extends Fragment implements Button.OnClickListener {
     51     private static final String TAG = "ProcessStatsDetail";
     52 
     53     public static final int ACTION_FORCE_STOP = 1;
     54 
     55     public static final String EXTRA_ENTRY = "entry";
     56     public static final String EXTRA_USE_USS = "use_uss";
     57     public static final String EXTRA_MAX_WEIGHT = "max_weight";
     58     public static final String EXTRA_TOTAL_TIME = "total_time";
     59 
     60     private PackageManager mPm;
     61     private DevicePolicyManager mDpm;
     62 
     63     private ProcStatsEntry mEntry;
     64     private boolean mUseUss;
     65     private long mMaxWeight;
     66     private long mTotalTime;
     67 
     68     private View mRootView;
     69     private TextView mTitleView;
     70     private ViewGroup mTwoButtonsPanel;
     71     private Button mForceStopButton;
     72     private Button mReportButton;
     73     private ViewGroup mDetailsParent;
     74     private ViewGroup mServicesParent;
     75 
     76     public static String makePercentString(Resources res, long amount, long total) {
     77         final double percent = (((double)amount) / total) * 100;
     78         return res.getString(R.string.percentage, (int) Math.round(percent));
     79     }
     80 
     81     @Override
     82     public void onCreate(Bundle icicle) {
     83         super.onCreate(icicle);
     84         mPm = getActivity().getPackageManager();
     85         mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
     86         final Bundle args = getArguments();
     87         mEntry = (ProcStatsEntry)args.getParcelable(EXTRA_ENTRY);
     88         mEntry.retrieveUiData(mPm);
     89         mUseUss = args.getBoolean(EXTRA_USE_USS);
     90         mMaxWeight = args.getLong(EXTRA_MAX_WEIGHT);
     91         mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
     92     }
     93 
     94     @Override
     95     public View onCreateView(
     96             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     97         final View view = inflater.inflate(R.layout.process_stats_details, container, false);
     98         prepareCustomPreferencesList(container, view, view, false);
     99 
    100         mRootView = view;
    101         createDetails();
    102         return view;
    103     }
    104 
    105     @Override
    106     public void onResume() {
    107         super.onResume();
    108         checkForceStop();
    109     }
    110 
    111     @Override
    112     public void onPause() {
    113         super.onPause();
    114     }
    115 
    116     private void createDetails() {
    117         final double percentOfWeight = (((double)mEntry.mWeight) / mMaxWeight) * 100;
    118 
    119         int appLevel = (int) Math.ceil(percentOfWeight);
    120         String appLevelText = makePercentString(getResources(), mEntry.mDuration, mTotalTime);
    121 
    122         // Set all values in the header.
    123         final TextView summary = (TextView) mRootView.findViewById(android.R.id.summary);
    124         summary.setText(mEntry.mName);
    125         summary.setVisibility(View.VISIBLE);
    126         mTitleView = (TextView) mRootView.findViewById(android.R.id.title);
    127         mTitleView.setText(mEntry.mUiBaseLabel);
    128         final TextView text1 = (TextView)mRootView.findViewById(android.R.id.text1);
    129         text1.setText(appLevelText);
    130         final ProgressBar progress = (ProgressBar) mRootView.findViewById(android.R.id.progress);
    131         progress.setProgress(appLevel);
    132         final ImageView icon = (ImageView) mRootView.findViewById(android.R.id.icon);
    133         if (mEntry.mUiTargetApp != null) {
    134             icon.setImageDrawable(mEntry.mUiTargetApp.loadIcon(mPm));
    135         }
    136 
    137         mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel);
    138         mForceStopButton = (Button)mRootView.findViewById(R.id.right_button);
    139         mReportButton = (Button)mRootView.findViewById(R.id.left_button);
    140         mForceStopButton.setEnabled(false);
    141         mReportButton.setVisibility(View.INVISIBLE);
    142 
    143         mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
    144         mServicesParent = (ViewGroup)mRootView.findViewById(R.id.services);
    145 
    146         fillDetailsSection();
    147         fillServicesSection();
    148 
    149         if (mEntry.mUid >= android.os.Process.FIRST_APPLICATION_UID) {
    150             mForceStopButton.setText(R.string.force_stop);
    151             mForceStopButton.setTag(ACTION_FORCE_STOP);
    152             mForceStopButton.setOnClickListener(this);
    153             mTwoButtonsPanel.setVisibility(View.VISIBLE);
    154         } else {
    155             mTwoButtonsPanel.setVisibility(View.GONE);
    156         }
    157     }
    158 
    159     public void onClick(View v) {
    160         doAction((Integer) v.getTag());
    161     }
    162 
    163     private void doAction(int action) {
    164         PreferenceActivity pa = (PreferenceActivity)getActivity();
    165         switch (action) {
    166             case ACTION_FORCE_STOP:
    167                 killProcesses();
    168                 break;
    169         }
    170     }
    171 
    172     private void addPackageHeaderItem(ViewGroup parent, String packageName) {
    173         LayoutInflater inflater = getActivity().getLayoutInflater();
    174         ViewGroup item = (ViewGroup) inflater.inflate(R.layout.running_processes_item,
    175                 null);
    176         parent.addView(item);
    177         final ImageView icon = (ImageView) item.findViewById(R.id.icon);
    178         TextView nameView = (TextView) item.findViewById(R.id.name);
    179         TextView descriptionView = (TextView) item.findViewById(R.id.description);
    180         try {
    181             ApplicationInfo ai = mPm.getApplicationInfo(packageName, 0);
    182             icon.setImageDrawable(ai.loadIcon(mPm));
    183             nameView.setText(ai.loadLabel(mPm));
    184         } catch (PackageManager.NameNotFoundException e) {
    185         }
    186         descriptionView.setText(packageName);
    187     }
    188 
    189     private void addDetailsItem(ViewGroup parent, CharSequence label, CharSequence value) {
    190         LayoutInflater inflater = getActivity().getLayoutInflater();
    191         ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
    192                 null);
    193         parent.addView(item);
    194         TextView labelView = (TextView) item.findViewById(R.id.label);
    195         TextView valueView = (TextView) item.findViewById(R.id.value);
    196         labelView.setText(label);
    197         valueView.setText(value);
    198     }
    199 
    200     private void fillDetailsSection() {
    201         addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_avg_ram_use),
    202                 Formatter.formatShortFileSize(getActivity(),
    203                         (mUseUss ? mEntry.mAvgUss : mEntry.mAvgPss) * 1024));
    204         addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_max_ram_use),
    205                 Formatter.formatShortFileSize(getActivity(),
    206                         (mUseUss ? mEntry.mMaxUss : mEntry.mMaxPss) * 1024));
    207         addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_run_time),
    208                 makePercentString(getResources(), mEntry.mDuration, mTotalTime));
    209     }
    210 
    211     final static Comparator<ProcStatsEntry.Service> sServiceCompare
    212             = new Comparator<ProcStatsEntry.Service>() {
    213         @Override
    214         public int compare(ProcStatsEntry.Service lhs, ProcStatsEntry.Service rhs) {
    215             if (lhs.mDuration < rhs.mDuration) {
    216                 return 1;
    217             } else if (lhs.mDuration > rhs.mDuration) {
    218                 return -1;
    219             }
    220             return 0;
    221         }
    222     };
    223 
    224     final static Comparator<ArrayList<ProcStatsEntry.Service>> sServicePkgCompare
    225             = new Comparator<ArrayList<ProcStatsEntry.Service>>() {
    226         @Override
    227         public int compare(ArrayList<ProcStatsEntry.Service> lhs,
    228                 ArrayList<ProcStatsEntry.Service> rhs) {
    229             long topLhs = lhs.size() > 0 ? lhs.get(0).mDuration : 0;
    230             long topRhs = rhs.size() > 0 ? rhs.get(0).mDuration : 0;
    231             if (topLhs < topRhs) {
    232                 return 1;
    233             } else if (topLhs > topRhs) {
    234                 return -1;
    235             }
    236             return 0;
    237         }
    238     };
    239 
    240     private void fillServicesSection() {
    241         if (mEntry.mServices.size() > 0) {
    242             boolean addPackageSections = false;
    243             // Sort it all.
    244             ArrayList<ArrayList<ProcStatsEntry.Service>> servicePkgs
    245                     = new ArrayList<ArrayList<ProcStatsEntry.Service>>();
    246             for (int ip=0; ip<mEntry.mServices.size(); ip++) {
    247                 ArrayList<ProcStatsEntry.Service> services =
    248                         (ArrayList<ProcStatsEntry.Service>)mEntry.mServices.valueAt(ip).clone();
    249                 Collections.sort(services, sServiceCompare);
    250                 servicePkgs.add(services);
    251             }
    252             if (mEntry.mServices.size() > 1
    253                     || !mEntry.mServices.valueAt(0).get(0).mPackage.equals(mEntry.mPackage)) {
    254                 addPackageSections = true;
    255                 // Sort these so that the one(s) with the longest run durations are on top.
    256                 Collections.sort(servicePkgs, sServicePkgCompare);
    257             }
    258             for (int ip=0; ip<servicePkgs.size(); ip++) {
    259                 ArrayList<ProcStatsEntry.Service> services = servicePkgs.get(ip);
    260                 if (addPackageSections) {
    261                     addPackageHeaderItem(mServicesParent, services.get(0).mPackage);
    262                 }
    263                 for (int is=0; is<services.size(); is++) {
    264                     ProcStatsEntry.Service service = services.get(is);
    265                     String label = service.mName;
    266                     int tail = label.lastIndexOf('.');
    267                     if (tail >= 0 && tail < (label.length()-1)) {
    268                         label = label.substring(tail+1);
    269                     }
    270                     long duration = service.mDuration;
    271                     final double percentOfTime = (((double)duration) / mTotalTime) * 100;
    272                     addDetailsItem(mServicesParent, label, getActivity().getResources().getString(
    273                             R.string.percentage, (int) Math.ceil(percentOfTime)));
    274                 }
    275             }
    276         }
    277     }
    278 
    279     private void killProcesses() {
    280         ActivityManager am = (ActivityManager)getActivity().getSystemService(
    281                 Context.ACTIVITY_SERVICE);
    282         am.forceStopPackage(mEntry.mUiPackage);
    283         checkForceStop();
    284     }
    285 
    286     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
    287         @Override
    288         public void onReceive(Context context, Intent intent) {
    289             mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
    290         }
    291     };
    292 
    293     private void checkForceStop() {
    294         if (mEntry.mUiPackage == null || mEntry.mUid < Process.FIRST_APPLICATION_UID) {
    295             mForceStopButton.setEnabled(false);
    296             return;
    297         }
    298         if (mDpm.packageHasActiveAdmins(mEntry.mUiPackage)) {
    299             mForceStopButton.setEnabled(false);
    300             return;
    301         }
    302         try {
    303             ApplicationInfo info = mPm.getApplicationInfo(mEntry.mUiPackage, 0);
    304             if ((info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
    305                 mForceStopButton.setEnabled(true);
    306             }
    307         } catch (PackageManager.NameNotFoundException e) {
    308         }
    309         Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
    310                 Uri.fromParts("package", mEntry.mUiPackage, null));
    311         intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mEntry.mUiPackage });
    312         intent.putExtra(Intent.EXTRA_UID, mEntry.mUid);
    313         intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mEntry.mUid));
    314         getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
    315                 Activity.RESULT_CANCELED, null, null);
    316     }
    317 }
    318