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