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