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