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