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.os.UserHandle; 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; 42import com.android.settings.Utils; 43 44import java.util.ArrayList; 45import java.util.Collections; 46import java.util.Comparator; 47 48import static com.android.settings.Utils.prepareCustomPreferencesList; 49 50public 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 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 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 101 public void onResume() { 102 super.onResume(); 103 checkForceStop(); 104 } 105 106 @Override 107 public void onPause() { 108 super.onPause(); 109 } 110 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 154 public void onClick(View v) { 155 doAction((Integer) v.getTag()); 156 } 157 158 private void doAction(int action) { 159 switch (action) { 160 case ACTION_FORCE_STOP: 161 killProcesses(); 162 break; 163 } 164 } 165 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 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 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 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 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 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