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