ProcessStatsDetail.java revision 04486738342fa5e11a78df836efe34b85bee125a
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_MAX_WEIGHT = "max_weight";
56    public static final String EXTRA_TOTAL_TIME = "total_time";
57
58    private PackageManager mPm;
59    private DevicePolicyManager mDpm;
60
61    private ProcStatsEntry mEntry;
62    private long mMaxWeight;
63    private long mTotalTime;
64
65    private View mRootView;
66    private TextView mTitleView;
67    private ViewGroup mTwoButtonsPanel;
68    private Button mForceStopButton;
69    private Button mReportButton;
70    private ViewGroup mDetailsParent;
71    private ViewGroup mServicesParent;
72
73    public static String makePercentString(Resources res, long amount, long total) {
74        final double percent = (((double)amount) / total) * 100;
75        return res.getString(R.string.percentage, (int) Math.ceil(percent));
76    }
77
78    @Override
79    public void onCreate(Bundle icicle) {
80        super.onCreate(icicle);
81        mPm = getActivity().getPackageManager();
82        mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
83        final Bundle args = getArguments();
84        mEntry = (ProcStatsEntry)args.getParcelable(EXTRA_ENTRY);
85        mEntry.retrieveUiData(mPm);
86        mMaxWeight = args.getLong(EXTRA_MAX_WEIGHT);
87        mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
88    }
89
90    @Override
91    public View onCreateView(
92            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
93        final View view = inflater.inflate(R.layout.process_stats_details, container, false);
94        prepareCustomPreferencesList(container, view, view, false);
95
96        mRootView = view;
97        createDetails();
98        return view;
99    }
100
101    @Override
102    public void onResume() {
103        super.onResume();
104        checkForceStop();
105    }
106
107    @Override
108    public void onPause() {
109        super.onPause();
110    }
111
112    private void createDetails() {
113        final double percentOfWeight = (((double)mEntry.mWeight) / mMaxWeight) * 100;
114
115        int appLevel = (int) Math.ceil(percentOfWeight);
116        String appLevelText = makePercentString(getResources(), mEntry.mDuration, mTotalTime);
117
118        // Set all values in the header.
119        final TextView summary = (TextView) mRootView.findViewById(android.R.id.summary);
120        summary.setText(mEntry.mName);
121        summary.setVisibility(View.VISIBLE);
122        mTitleView = (TextView) mRootView.findViewById(android.R.id.title);
123        mTitleView.setText(mEntry.mUiBaseLabel);
124        final TextView text1 = (TextView)mRootView.findViewById(android.R.id.text1);
125        text1.setText(appLevelText);
126        final ProgressBar progress = (ProgressBar) mRootView.findViewById(android.R.id.progress);
127        progress.setProgress(appLevel);
128        final ImageView icon = (ImageView) mRootView.findViewById(android.R.id.icon);
129        if (mEntry.mUiTargetApp != null) {
130            icon.setImageDrawable(mEntry.mUiTargetApp.loadIcon(mPm));
131        }
132
133        mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel);
134        mForceStopButton = (Button)mRootView.findViewById(R.id.right_button);
135        mReportButton = (Button)mRootView.findViewById(R.id.left_button);
136        mForceStopButton.setEnabled(false);
137        mReportButton.setVisibility(View.INVISIBLE);
138
139        mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
140        mServicesParent = (ViewGroup)mRootView.findViewById(R.id.services);
141
142        fillDetailsSection();
143        fillServicesSection();
144
145        if (mEntry.mUid >= android.os.Process.FIRST_APPLICATION_UID) {
146            mForceStopButton.setText(R.string.force_stop);
147            mForceStopButton.setTag(ACTION_FORCE_STOP);
148            mForceStopButton.setOnClickListener(this);
149            mTwoButtonsPanel.setVisibility(View.VISIBLE);
150        } else {
151            mTwoButtonsPanel.setVisibility(View.GONE);
152        }
153    }
154
155    public void onClick(View v) {
156        doAction((Integer) v.getTag());
157    }
158
159    private void doAction(int action) {
160        PreferenceActivity pa = (PreferenceActivity)getActivity();
161        switch (action) {
162            case ACTION_FORCE_STOP:
163                killProcesses();
164                break;
165        }
166    }
167
168    private void addDetailsItem(ViewGroup parent, CharSequence label, CharSequence value) {
169        LayoutInflater inflater = getActivity().getLayoutInflater();
170        ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
171                null);
172        parent.addView(item);
173        TextView labelView = (TextView) item.findViewById(R.id.label);
174        TextView valueView = (TextView) item.findViewById(R.id.value);
175        labelView.setText(label);
176        valueView.setText(value);
177    }
178
179    private void fillDetailsSection() {
180        addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_ram_use),
181                Formatter.formatShortFileSize(getActivity(), mEntry.mAvgPss * 1024));
182        addDetailsItem(mDetailsParent, getResources().getText(R.string.process_stats_run_time),
183                makePercentString(getResources(), mEntry.mDuration, mTotalTime));
184    }
185
186    final static Comparator<ProcStatsEntry.Service> sServiceCompare
187            = new Comparator<ProcStatsEntry.Service>() {
188        @Override
189        public int compare(ProcStatsEntry.Service lhs, ProcStatsEntry.Service rhs) {
190            if (lhs.mDuration < rhs.mDuration) {
191                return 1;
192            } else if (lhs.mDuration > rhs.mDuration) {
193                return -1;
194            }
195            return 0;
196        }
197    };
198
199    private void fillServicesSection() {
200        LayoutInflater inflater = getActivity().getLayoutInflater();
201        if (mEntry.mServices.size() > 0) {
202            ArrayList<ProcStatsEntry.Service> services =
203                    (ArrayList<ProcStatsEntry.Service>)mEntry.mServices.clone();
204            Collections.sort(services, sServiceCompare);
205            for (int i=0; i<services.size(); i++) {
206                ProcStatsEntry.Service service = services.get(i);
207                String label = service.mName;
208                int tail = label.lastIndexOf('.');
209                if (tail >= 0 && tail < (label.length()-1)) {
210                    label = label.substring(tail+1);
211                }
212                long duration = service.mDuration;
213                final double percentOfTime = (((double)duration) / mTotalTime) * 100;
214                addDetailsItem(mServicesParent, label, getActivity().getResources().getString(
215                        R.string.percentage, (int) Math.ceil(percentOfTime)));
216            }
217        }
218    }
219
220    private void killProcesses() {
221        ActivityManager am = (ActivityManager)getActivity().getSystemService(
222                Context.ACTIVITY_SERVICE);
223        am.forceStopPackage(mEntry.mUiPackage);
224        checkForceStop();
225    }
226
227    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
228        @Override
229        public void onReceive(Context context, Intent intent) {
230            mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
231        }
232    };
233
234    private void checkForceStop() {
235        if (mEntry.mUiPackage == null || mEntry.mUid < Process.FIRST_APPLICATION_UID) {
236            mForceStopButton.setEnabled(false);
237            return;
238        }
239        if (mDpm.packageHasActiveAdmins(mEntry.mUiPackage)) {
240            mForceStopButton.setEnabled(false);
241            return;
242        }
243        try {
244            ApplicationInfo info = mPm.getApplicationInfo(mEntry.mUiPackage, 0);
245            if ((info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
246                mForceStopButton.setEnabled(true);
247            }
248        } catch (PackageManager.NameNotFoundException e) {
249        }
250        Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
251                Uri.fromParts("package", mEntry.mUiPackage, null));
252        intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mEntry.mUiPackage });
253        intent.putExtra(Intent.EXTRA_UID, mEntry.mUid);
254        intent.putExtra(Intent.EXTRA_USER_HANDLE, mEntry.mUid);
255        getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
256                Activity.RESULT_CANCELED, null, null);
257    }
258}
259