PowerUsageDetail.java revision 59a48604b5a803fbec6857e07f9fa1adbc6b8bb4
1/* 2 * Copyright (C) 2009 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.fuelgauge; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.app.ApplicationErrorReport; 22import android.app.Fragment; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.pm.ApplicationInfo; 28import android.content.pm.PackageInfo; 29import android.content.pm.PackageManager; 30import android.content.pm.PackageManager.NameNotFoundException; 31import android.content.res.Resources; 32import android.graphics.drawable.Drawable; 33import android.net.Uri; 34import android.os.Bundle; 35import android.os.Process; 36import android.preference.PreferenceActivity; 37import android.provider.Settings; 38import android.text.TextUtils; 39import android.view.LayoutInflater; 40import android.view.View; 41import android.view.ViewGroup; 42import android.widget.Button; 43import android.widget.ImageView; 44import android.widget.TextView; 45import com.android.settings.R; 46import com.android.settings.applications.InstalledAppDetails; 47 48public class PowerUsageDetail extends Fragment implements Button.OnClickListener { 49 50 enum DrainType { 51 IDLE, 52 CELL, 53 PHONE, 54 WIFI, 55 BLUETOOTH, 56 SCREEN, 57 APP 58 } 59 60 // Note: Must match the sequence of the DrainType 61 private static int[] sDrainTypeDesciptions = new int[] { 62 R.string.battery_desc_standby, 63 R.string.battery_desc_radio, 64 R.string.battery_desc_voice, 65 R.string.battery_desc_wifi, 66 R.string.battery_desc_bluetooth, 67 R.string.battery_desc_display, 68 R.string.battery_desc_apps 69 }; 70 71 public static final int ACTION_DISPLAY_SETTINGS = 1; 72 public static final int ACTION_WIFI_SETTINGS = 2; 73 public static final int ACTION_BLUETOOTH_SETTINGS = 3; 74 public static final int ACTION_WIRELESS_SETTINGS = 4; 75 public static final int ACTION_APP_DETAILS = 5; 76 public static final int ACTION_SECURITY_SETTINGS = 6; 77 public static final int ACTION_FORCE_STOP = 7; 78 public static final int ACTION_REPORT = 8; 79 80 public static final int USAGE_SINCE_UNPLUGGED = 1; 81 public static final int USAGE_SINCE_RESET = 2; 82 83 public static final String EXTRA_TITLE = "title"; 84 public static final String EXTRA_PERCENT = "percent"; 85 public static final String EXTRA_GAUGE = "gauge"; 86 public static final String EXTRA_UID = "uid"; 87 public static final String EXTRA_USAGE_SINCE = "since"; 88 public static final String EXTRA_USAGE_DURATION = "duration"; 89 public static final String EXTRA_REPORT_DETAILS = "report_details"; 90 public static final String EXTRA_REPORT_CHECKIN_DETAILS = "report_checkin_details"; 91 public static final String EXTRA_DETAIL_TYPES = "types"; // Array of usage types (cpu, gps, etc) 92 public static final String EXTRA_DETAIL_VALUES = "values"; // Array of doubles 93 public static final String EXTRA_DRAIN_TYPE = "drainType"; // DrainType 94 public static final String EXTRA_ICON_PACKAGE = "iconPackage"; // String 95 public static final String EXTRA_NO_COVERAGE = "noCoverage"; 96 public static final String EXTRA_ICON_ID = "iconId"; // Int 97 98 private static final boolean DEBUG = true; 99 private String mTitle; 100 private int mUsageSince; 101 private int[] mTypes; 102 private int mUid; 103 private double[] mValues; 104 private View mRootView; 105 private TextView mTitleView; 106 private ViewGroup mTwoButtonsPanel; 107 private Button mForceStopButton; 108 private Button mReportButton; 109 private ViewGroup mDetailsParent; 110 private ViewGroup mControlsParent; 111 private long mStartTime; 112 private DrainType mDrainType; 113 private PercentageBar mGauge; 114 private Drawable mAppIcon; 115 private double mNoCoverage; // Percentage of time that there was no coverage 116 117 private boolean mUsesGps; 118 119 private static final String TAG = "PowerUsageDetail"; 120 private String[] mPackages; 121 122 ApplicationInfo mApp; 123 ComponentName mInstaller; 124 125 @Override 126 public void onCreate(Bundle icicle) { 127 super.onCreate(icicle); 128 } 129 130 @Override 131 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 132 View view = mRootView = inflater.inflate(R.layout.power_usage_details, null); 133 createDetails(); 134 return view; 135 } 136 137 @Override 138 public void onResume() { 139 super.onResume(); 140 mStartTime = android.os.Process.getElapsedCpuTime(); 141 checkForceStop(); 142 } 143 144 @Override 145 public void onPause() { 146 super.onPause(); 147 } 148 149 private void createDetails() { 150 final Bundle args = getArguments(); 151 mTitle = args.getString(EXTRA_TITLE); 152 final int percentage = args.getInt(EXTRA_PERCENT, 1); 153 final int gaugeValue = args.getInt(EXTRA_GAUGE, 1); 154 mUsageSince = args.getInt(EXTRA_USAGE_SINCE, USAGE_SINCE_UNPLUGGED); 155 mUid = args.getInt(EXTRA_UID, 0); 156 mDrainType = (DrainType) args.getSerializable(EXTRA_DRAIN_TYPE); 157 mNoCoverage = args.getDouble(EXTRA_NO_COVERAGE, 0); 158 String iconPackage = args.getString(EXTRA_ICON_PACKAGE); 159 int iconId = args.getInt(EXTRA_ICON_ID, 0); 160 if (!TextUtils.isEmpty(iconPackage)) { 161 try { 162 final PackageManager pm = getActivity().getPackageManager(); 163 ApplicationInfo ai = pm.getPackageInfo(iconPackage, 0).applicationInfo; 164 if (ai != null) { 165 mAppIcon = ai.loadIcon(pm); 166 } 167 } catch (NameNotFoundException nnfe) { 168 // Use default icon 169 } 170 } else if (iconId != 0) { 171 mAppIcon = getActivity().getResources().getDrawable(iconId); 172 } 173 if (mAppIcon == null) { 174 mAppIcon = getActivity().getPackageManager().getDefaultActivityIcon(); 175 } 176 177 // Set the description 178 String summary = getDescriptionForDrainType(); 179 ((TextView)mRootView.findViewById(R.id.summary)).setText(summary); 180 181 mTypes = args.getIntArray(EXTRA_DETAIL_TYPES); 182 mValues = args.getDoubleArray(EXTRA_DETAIL_VALUES); 183 184 mTitleView = (TextView)mRootView.findViewById(R.id.name); 185 mTitleView.setText(mTitle); 186 ((TextView)mRootView.findViewById(R.id.battery_percentage)) 187 .setText(String.format("%d%%", percentage)); 188 189 mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel); 190 mForceStopButton = (Button)mRootView.findViewById(R.id.left_button); 191 mReportButton = (Button)mRootView.findViewById(R.id.right_button); 192 mForceStopButton.setEnabled(false); 193 194 ImageView gaugeImage = (ImageView)mRootView.findViewById(R.id.gauge); 195 mGauge = new PercentageBar(); 196 mGauge.percent = gaugeValue; 197 mGauge.bar = getResources().getDrawable(R.drawable.app_gauge); 198 gaugeImage.setImageDrawable(mGauge); 199 200 ImageView iconImage = (ImageView)mRootView.findViewById(R.id.icon); 201 iconImage.setImageDrawable(mAppIcon); 202 203 mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details); 204 mControlsParent = (ViewGroup)mRootView.findViewById(R.id.controls); 205 206 fillDetailsSection(); 207 fillPackagesSection(mUid); 208 fillControlsSection(mUid); 209 210 if (mUid >= Process.FIRST_APPLICATION_UID) { 211 mForceStopButton.setText(R.string.force_stop); 212 mForceStopButton.setTag(ACTION_FORCE_STOP); 213 mForceStopButton.setOnClickListener(this); 214 mReportButton.setText(com.android.internal.R.string.report); 215 mReportButton.setTag(ACTION_REPORT); 216 mReportButton.setOnClickListener(this); 217 218 // check if error reporting is enabled in secure settings 219 int enabled = Settings.Secure.getInt(getActivity().getContentResolver(), 220 Settings.Secure.SEND_ACTION_APP_ERROR, 0); 221 if (enabled != 0) { 222 if (mPackages != null && mPackages.length > 0) { 223 try { 224 mApp = getActivity().getPackageManager().getApplicationInfo( 225 mPackages[0], 0); 226 mInstaller = ApplicationErrorReport.getErrorReportReceiver( 227 getActivity(), mPackages[0], mApp.flags); 228 } catch (NameNotFoundException e) { 229 } 230 } 231 mReportButton.setEnabled(mInstaller != null); 232 } else { 233 mTwoButtonsPanel.setVisibility(View.GONE); 234 } 235 } else { 236 mTwoButtonsPanel.setVisibility(View.GONE); 237 } 238 } 239 240 public void onClick(View v) { 241 doAction((Integer) v.getTag()); 242 } 243 244 // utility method used to start sub activity 245 private void startApplicationDetailsActivity() { 246 // start new fragment to display extended information 247 Bundle args = new Bundle(); 248 args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mPackages[0]); 249 250 PreferenceActivity pa = (PreferenceActivity)getActivity(); 251 pa.startPreferencePanel(InstalledAppDetails.class.getName(), args, 252 R.string.application_info_label, null, null, 0); 253 } 254 255 private void doAction(int action) { 256 switch (action) { 257 case ACTION_DISPLAY_SETTINGS: 258 startActivity(new Intent(Settings.ACTION_DISPLAY_SETTINGS)); 259 break; 260 case ACTION_WIFI_SETTINGS: 261 startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); 262 break; 263 case ACTION_BLUETOOTH_SETTINGS: 264 startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)); 265 break; 266 case ACTION_WIRELESS_SETTINGS: 267 startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); 268 break; 269 case ACTION_APP_DETAILS: 270 startApplicationDetailsActivity(); 271 break; 272 case ACTION_SECURITY_SETTINGS: 273 startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS)); 274 break; 275 case ACTION_FORCE_STOP: 276 killProcesses(); 277 break; 278 case ACTION_REPORT: 279 reportBatteryUse(); 280 break; 281 } 282 } 283 284 private void fillDetailsSection() { 285 LayoutInflater inflater = getActivity().getLayoutInflater(); 286 if (mTypes != null && mValues != null) { 287 for (int i = 0; i < mTypes.length; i++) { 288 // Only add an item if the time is greater than zero 289 if (mValues[i] <= 0) continue; 290 final String label = getString(mTypes[i]); 291 String value = null; 292 switch (mTypes[i]) { 293 case R.string.usage_type_data_recv: 294 case R.string.usage_type_data_send: 295 value = Utils.formatBytes(getActivity(), mValues[i]); 296 break; 297 case R.string.usage_type_no_coverage: 298 value = String.format("%d%%", (int) Math.floor(mValues[i])); 299 break; 300 case R.string.usage_type_gps: 301 mUsesGps = true; 302 // Fall through 303 default: 304 value = Utils.formatElapsedTime(getActivity(), mValues[i]); 305 } 306 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text, 307 null); 308 mDetailsParent.addView(item); 309 TextView labelView = (TextView) item.findViewById(R.id.label); 310 TextView valueView = (TextView) item.findViewById(R.id.value); 311 labelView.setText(label); 312 valueView.setText(value); 313 } 314 } 315 } 316 317 private void fillControlsSection(int uid) { 318 PackageManager pm = getActivity().getPackageManager(); 319 String[] packages = pm.getPackagesForUid(uid); 320 PackageInfo pi = null; 321 try { 322 pi = packages != null ? pm.getPackageInfo(packages[0], 0) : null; 323 } catch (NameNotFoundException nnfe) { /* Nothing */ } 324 ApplicationInfo ai = pi != null? pi.applicationInfo : null; 325 boolean isSystem = ai != null? (ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0 : false; 326 327 boolean removeHeader = true; 328 switch (mDrainType) { 329 case APP: 330 // If it is a Java application and it's not a system application 331 if (packages != null && !isSystem) { 332 addControl(R.string.battery_action_app_details, 333 R.string.battery_sugg_apps_info, ACTION_APP_DETAILS); 334 removeHeader = false; 335 // If the application has a settings screen, jump to that 336 // TODO: 337 } 338 if (mUsesGps) { 339 addControl(R.string.security_settings_title, 340 R.string.battery_sugg_apps_gps, ACTION_SECURITY_SETTINGS); 341 removeHeader = false; 342 } 343 break; 344 case SCREEN: 345 addControl(R.string.display_settings, 346 R.string.battery_sugg_display, 347 ACTION_DISPLAY_SETTINGS); 348 removeHeader = false; 349 break; 350 case WIFI: 351 addControl(R.string.wifi_settings, 352 R.string.battery_sugg_wifi, 353 ACTION_WIFI_SETTINGS); 354 removeHeader = false; 355 break; 356 case BLUETOOTH: 357 addControl(R.string.bluetooth_settings, 358 R.string.battery_sugg_bluetooth_basic, 359 ACTION_BLUETOOTH_SETTINGS); 360 removeHeader = false; 361 break; 362 case CELL: 363 if (mNoCoverage > 10) { 364 addControl(R.string.radio_controls_title, 365 R.string.battery_sugg_radio, 366 ACTION_WIRELESS_SETTINGS); 367 removeHeader = false; 368 } 369 break; 370 } 371 if (removeHeader) { 372 mControlsParent.setVisibility(View.GONE); 373 } 374 } 375 376 private void addControl(int title, int summary, int action) { 377 final Resources res = getResources(); 378 LayoutInflater inflater = getActivity().getLayoutInflater(); 379 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_action_item,null); 380 mControlsParent.addView(item); 381 Button actionButton = (Button) item.findViewById(R.id.action_button); 382 TextView summaryView = (TextView) item.findViewById(R.id.summary); 383 actionButton.setText(res.getString(title)); 384 summaryView.setText(res.getString(summary)); 385 actionButton.setOnClickListener(this); 386 actionButton.setTag(new Integer(action)); 387 } 388 389 private void removePackagesSection() { 390 View view; 391 if ((view = mRootView.findViewById(R.id.packages_section_title)) != null) { 392 view.setVisibility(View.GONE); 393 } 394 if ((view = mRootView.findViewById(R.id.packages_section)) != null) { 395 view.setVisibility(View.GONE); 396 } 397 } 398 399 private void killProcesses() { 400 if (mPackages == null) return; 401 ActivityManager am = (ActivityManager)getActivity().getSystemService( 402 Context.ACTIVITY_SERVICE); 403 for (int i = 0; i < mPackages.length; i++) { 404 am.forceStopPackage(mPackages[i]); 405 } 406 checkForceStop(); 407 } 408 409 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 410 @Override 411 public void onReceive(Context context, Intent intent) { 412 mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED); 413 } 414 }; 415 416 private void checkForceStop() { 417 if (mPackages == null || mUid < Process.FIRST_APPLICATION_UID) { 418 mForceStopButton.setEnabled(false); 419 return; 420 } 421 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 422 Uri.fromParts("package", mPackages[0], null)); 423 intent.putExtra(Intent.EXTRA_PACKAGES, mPackages); 424 intent.putExtra(Intent.EXTRA_UID, mUid); 425 getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null, 426 Activity.RESULT_CANCELED, null, null); 427 } 428 429 private void reportBatteryUse() { 430 if (mPackages == null) return; 431 432 ApplicationErrorReport report = new ApplicationErrorReport(); 433 report.type = ApplicationErrorReport.TYPE_BATTERY; 434 report.packageName = mPackages[0]; 435 report.installerPackageName = mInstaller.getPackageName(); 436 report.processName = mPackages[0]; 437 report.time = System.currentTimeMillis(); 438 report.systemApp = (mApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 439 440 final Bundle args = getArguments(); 441 ApplicationErrorReport.BatteryInfo batteryInfo = new ApplicationErrorReport.BatteryInfo(); 442 batteryInfo.usagePercent = args.getInt(EXTRA_PERCENT, 1); 443 batteryInfo.durationMicros = args.getLong(EXTRA_USAGE_DURATION, 0); 444 batteryInfo.usageDetails = args.getString(EXTRA_REPORT_DETAILS); 445 batteryInfo.checkinDetails = args.getString(EXTRA_REPORT_CHECKIN_DETAILS); 446 report.batteryInfo = batteryInfo; 447 448 Intent result = new Intent(Intent.ACTION_APP_ERROR); 449 result.setComponent(mInstaller); 450 result.putExtra(Intent.EXTRA_BUG_REPORT, report); 451 result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 452 startActivity(result); 453 } 454 455 private void fillPackagesSection(int uid) { 456 if (uid < 1) { 457 removePackagesSection(); 458 return; 459 } 460 ViewGroup packagesParent = (ViewGroup)mRootView.findViewById(R.id.packages_section); 461 if (packagesParent == null) return; 462 LayoutInflater inflater = getActivity().getLayoutInflater(); 463 464 PackageManager pm = getActivity().getPackageManager(); 465 //final Drawable defaultActivityIcon = pm.getDefaultActivityIcon(); 466 mPackages = pm.getPackagesForUid(uid); 467 if (mPackages == null || mPackages.length < 2) { 468 removePackagesSection(); 469 return; 470 } 471 472 // Convert package names to user-facing labels where possible 473 for (int i = 0; i < mPackages.length; i++) { 474 try { 475 ApplicationInfo ai = pm.getApplicationInfo(mPackages[i], 0); 476 CharSequence label = ai.loadLabel(pm); 477 //Drawable icon = defaultActivityIcon; 478 if (label != null) { 479 mPackages[i] = label.toString(); 480 } 481 //if (ai.icon != 0) { 482 // icon = ai.loadIcon(pm); 483 //} 484 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_package_item, 485 null); 486 packagesParent.addView(item); 487 TextView labelView = (TextView) item.findViewById(R.id.label); 488 labelView.setText(mPackages[i]); 489 } catch (NameNotFoundException e) { 490 } 491 } 492 } 493 494 private String getDescriptionForDrainType() { 495 return getResources().getString(sDrainTypeDesciptions[mDrainType.ordinal()]); 496 } 497} 498