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 static android.os.BatteryStats.NETWORK_MOBILE_RX_BYTES; 20 import static android.os.BatteryStats.NETWORK_MOBILE_TX_BYTES; 21 import static android.os.BatteryStats.NETWORK_WIFI_RX_BYTES; 22 import static android.os.BatteryStats.NETWORK_WIFI_TX_BYTES; 23 24 import android.app.Activity; 25 import android.content.Context; 26 import android.content.pm.UserInfo; 27 import android.graphics.drawable.Drawable; 28 import android.hardware.Sensor; 29 import android.hardware.SensorManager; 30 import android.os.BatteryStats; 31 import android.os.BatteryStats.Uid; 32 import android.os.Bundle; 33 import android.os.Handler; 34 import android.os.Parcel; 35 import android.os.Process; 36 import android.os.RemoteException; 37 import android.os.ServiceManager; 38 import android.os.SystemClock; 39 import android.os.UserHandle; 40 import android.os.UserManager; 41 import android.preference.PreferenceActivity; 42 import android.telephony.SignalStrength; 43 import android.util.Log; 44 import android.util.SparseArray; 45 46 import com.android.internal.app.IBatteryStats; 47 import com.android.internal.os.BatteryStatsImpl; 48 import com.android.internal.os.PowerProfile; 49 import com.android.internal.util.FastPrintWriter; 50 import com.android.settings.R; 51 import com.android.settings.fuelgauge.PowerUsageDetail.DrainType; 52 import com.android.settings.users.UserUtils; 53 54 import java.io.PrintWriter; 55 import java.io.StringWriter; 56 import java.io.Writer; 57 import java.util.ArrayList; 58 import java.util.Collections; 59 import java.util.List; 60 import java.util.Map; 61 62 /** 63 * A helper class for retrieving the power usage information for all applications and services. 64 * 65 * The caller must initialize this class as soon as activity object is ready to use (for example, in 66 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). 67 */ 68 public class BatteryStatsHelper { 69 70 private static final boolean DEBUG = false; 71 72 private static final String TAG = BatteryStatsHelper.class.getSimpleName(); 73 74 private static BatteryStatsImpl sStatsXfer; 75 private IBatteryStats mBatteryInfo; 76 private UserManager mUm; 77 private BatteryStatsImpl mStats; 78 private PowerProfile mPowerProfile; 79 80 private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>(); 81 private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>(); 82 private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>(); 83 private final SparseArray<List<BatterySipper>> mUserSippers 84 = new SparseArray<List<BatterySipper>>(); 85 private final SparseArray<Double> mUserPower = new SparseArray<Double>(); 86 87 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; 88 89 private long mStatsPeriod = 0; 90 private double mMaxPower = 1; 91 private double mTotalPower; 92 private double mWifiPower; 93 private double mBluetoothPower; 94 95 // How much the apps together have left WIFI running. 96 private long mAppWifiRunning; 97 98 /** Queue for fetching name and icon for an application */ 99 private ArrayList<BatterySipper> mRequestQueue = new ArrayList<BatterySipper>(); 100 101 private Activity mActivity; 102 private Handler mHandler; 103 104 private class NameAndIconLoader extends Thread { 105 private boolean mAbort = false; 106 107 public NameAndIconLoader() { 108 super("BatteryUsage Icon Loader"); 109 } 110 111 public void abort() { 112 mAbort = true; 113 } 114 115 @Override 116 public void run() { 117 while (true) { 118 BatterySipper bs; 119 synchronized (mRequestQueue) { 120 if (mRequestQueue.isEmpty() || mAbort) { 121 mHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN); 122 return; 123 } 124 bs = mRequestQueue.remove(0); 125 } 126 bs.loadNameAndIcon(); 127 } 128 } 129 } 130 131 private NameAndIconLoader mRequestThread; 132 133 public BatteryStatsHelper(Activity activity, Handler handler) { 134 mActivity = activity; 135 mHandler = handler; 136 } 137 138 /** Clears the current stats and forces recreating for future use. */ 139 public void clearStats() { 140 mStats = null; 141 } 142 143 public BatteryStatsImpl getStats() { 144 if (mStats == null) { 145 load(); 146 } 147 return mStats; 148 } 149 150 public PowerProfile getPowerProfile() { 151 return mPowerProfile; 152 } 153 154 public void create(Bundle icicle) { 155 if (icicle != null) { 156 mStats = sStatsXfer; 157 } 158 mBatteryInfo = IBatteryStats.Stub.asInterface( 159 ServiceManager.getService(BatteryStats.SERVICE_NAME)); 160 mUm = (UserManager) mActivity.getSystemService(Context.USER_SERVICE); 161 mPowerProfile = new PowerProfile(mActivity); 162 } 163 164 public void pause() { 165 if (mRequestThread != null) { 166 mRequestThread.abort(); 167 } 168 } 169 170 public void destroy() { 171 if (mActivity.isChangingConfigurations()) { 172 sStatsXfer = mStats; 173 } else { 174 BatterySipper.sUidCache.clear(); 175 } 176 } 177 178 public void startBatteryDetailPage( 179 PreferenceActivity caller, BatterySipper sipper, boolean showLocationButton) { 180 // Initialize mStats if necessary. 181 getStats(); 182 183 Bundle args = new Bundle(); 184 args.putString(PowerUsageDetail.EXTRA_TITLE, sipper.name); 185 args.putInt(PowerUsageDetail.EXTRA_PERCENT, (int) 186 Math.ceil(sipper.getSortValue() * 100 / mTotalPower)); 187 args.putInt(PowerUsageDetail.EXTRA_GAUGE, (int) 188 Math.ceil(sipper.getSortValue() * 100 / mMaxPower)); 189 args.putLong(PowerUsageDetail.EXTRA_USAGE_DURATION, mStatsPeriod); 190 args.putString(PowerUsageDetail.EXTRA_ICON_PACKAGE, sipper.defaultPackageName); 191 args.putInt(PowerUsageDetail.EXTRA_ICON_ID, sipper.iconId); 192 args.putDouble(PowerUsageDetail.EXTRA_NO_COVERAGE, sipper.noCoveragePercent); 193 if (sipper.uidObj != null) { 194 args.putInt(PowerUsageDetail.EXTRA_UID, sipper.uidObj.getUid()); 195 } 196 args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, sipper.drainType); 197 args.putBoolean(PowerUsageDetail.EXTRA_SHOW_LOCATION_BUTTON, showLocationButton); 198 199 int[] types; 200 double[] values; 201 switch (sipper.drainType) { 202 case APP: 203 case USER: 204 { 205 Uid uid = sipper.uidObj; 206 types = new int[] { 207 R.string.usage_type_cpu, 208 R.string.usage_type_cpu_foreground, 209 R.string.usage_type_wake_lock, 210 R.string.usage_type_gps, 211 R.string.usage_type_wifi_running, 212 R.string.usage_type_data_recv, 213 R.string.usage_type_data_send, 214 R.string.usage_type_data_wifi_recv, 215 R.string.usage_type_data_wifi_send, 216 R.string.usage_type_audio, 217 R.string.usage_type_video, 218 }; 219 values = new double[] { 220 sipper.cpuTime, 221 sipper.cpuFgTime, 222 sipper.wakeLockTime, 223 sipper.gpsTime, 224 sipper.wifiRunningTime, 225 sipper.mobileRxBytes, 226 sipper.mobileTxBytes, 227 sipper.wifiRxBytes, 228 sipper.wifiTxBytes, 229 0, 230 0 231 }; 232 233 if (sipper.drainType == DrainType.APP) { 234 Writer result = new StringWriter(); 235 PrintWriter printWriter = new FastPrintWriter(result, false, 1024); 236 mStats.dumpLocked(printWriter, "", mStatsType, uid.getUid()); 237 printWriter.flush(); 238 args.putString(PowerUsageDetail.EXTRA_REPORT_DETAILS, result.toString()); 239 240 result = new StringWriter(); 241 printWriter = new FastPrintWriter(result, false, 1024); 242 mStats.dumpCheckinLocked(printWriter, mStatsType, uid.getUid()); 243 printWriter.flush(); 244 args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS, 245 result.toString()); 246 } 247 } 248 break; 249 case CELL: 250 { 251 types = new int[] { 252 R.string.usage_type_on_time, 253 R.string.usage_type_no_coverage 254 }; 255 values = new double[] { 256 sipper.usageTime, 257 sipper.noCoveragePercent 258 }; 259 } 260 break; 261 case WIFI: 262 { 263 types = new int[] { 264 R.string.usage_type_wifi_running, 265 R.string.usage_type_cpu, 266 R.string.usage_type_cpu_foreground, 267 R.string.usage_type_wake_lock, 268 R.string.usage_type_data_recv, 269 R.string.usage_type_data_send, 270 R.string.usage_type_data_wifi_recv, 271 R.string.usage_type_data_wifi_send, 272 }; 273 values = new double[] { 274 sipper.usageTime, 275 sipper.cpuTime, 276 sipper.cpuFgTime, 277 sipper.wakeLockTime, 278 sipper.mobileRxBytes, 279 sipper.mobileTxBytes, 280 sipper.wifiRxBytes, 281 sipper.wifiTxBytes, 282 }; 283 } break; 284 case BLUETOOTH: 285 { 286 types = new int[] { 287 R.string.usage_type_on_time, 288 R.string.usage_type_cpu, 289 R.string.usage_type_cpu_foreground, 290 R.string.usage_type_wake_lock, 291 R.string.usage_type_data_recv, 292 R.string.usage_type_data_send, 293 R.string.usage_type_data_wifi_recv, 294 R.string.usage_type_data_wifi_send, 295 }; 296 values = new double[] { 297 sipper.usageTime, 298 sipper.cpuTime, 299 sipper.cpuFgTime, 300 sipper.wakeLockTime, 301 sipper.mobileRxBytes, 302 sipper.mobileTxBytes, 303 sipper.wifiRxBytes, 304 sipper.wifiTxBytes, 305 }; 306 } break; 307 default: 308 { 309 types = new int[] { 310 R.string.usage_type_on_time 311 }; 312 values = new double[] { 313 sipper.usageTime 314 }; 315 } 316 } 317 args.putIntArray(PowerUsageDetail.EXTRA_DETAIL_TYPES, types); 318 args.putDoubleArray(PowerUsageDetail.EXTRA_DETAIL_VALUES, values); 319 caller.startPreferencePanel(PowerUsageDetail.class.getName(), args, 320 R.string.details_title, null, null, 0); 321 } 322 323 /** 324 * Refreshes the power usage list. 325 * @param includeZeroConsumption whether includes those applications which have consumed very 326 * little power up till now. 327 */ 328 public void refreshStats(boolean includeZeroConsumption) { 329 // Initialize mStats if necessary. 330 getStats(); 331 332 mMaxPower = 0; 333 mTotalPower = 0; 334 mWifiPower = 0; 335 mBluetoothPower = 0; 336 mAppWifiRunning = 0; 337 338 mUsageList.clear(); 339 mWifiSippers.clear(); 340 mBluetoothSippers.clear(); 341 mUserSippers.clear(); 342 mUserPower.clear(); 343 344 processAppUsage(includeZeroConsumption); 345 processMiscUsage(); 346 347 Collections.sort(mUsageList); 348 349 if (mHandler != null) { 350 synchronized (mRequestQueue) { 351 if (!mRequestQueue.isEmpty()) { 352 if (mRequestThread != null) { 353 mRequestThread.abort(); 354 } 355 mRequestThread = new NameAndIconLoader(); 356 mRequestThread.setPriority(Thread.MIN_PRIORITY); 357 mRequestThread.start(); 358 mRequestQueue.notify(); 359 } 360 } 361 } 362 } 363 364 private void processAppUsage(boolean includeZeroConsumption) { 365 SensorManager sensorManager = (SensorManager) mActivity.getSystemService( 366 Context.SENSOR_SERVICE); 367 final int which = mStatsType; 368 final int speedSteps = mPowerProfile.getNumSpeedSteps(); 369 final double[] powerCpuNormal = new double[speedSteps]; 370 final long[] cpuSpeedStepTimes = new long[speedSteps]; 371 for (int p = 0; p < speedSteps; p++) { 372 powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); 373 } 374 final double mobilePowerPerByte = getMobilePowerPerByte(); 375 final double wifiPowerPerByte = getWifiPowerPerByte(); 376 long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which); 377 long appWakelockTime = 0; 378 BatterySipper osApp = null; 379 mStatsPeriod = uSecTime; 380 SparseArray<? extends Uid> uidStats = mStats.getUidStats(); 381 final int NU = uidStats.size(); 382 for (int iu = 0; iu < NU; iu++) { 383 Uid u = uidStats.valueAt(iu); 384 double p; // in mAs 385 double power = 0; // in mAs 386 double highestDrain = 0; 387 String packageWithHighestDrain = null; 388 //mUsageList.add(new AppUsage(u.getUid(), new double[] {power})); 389 Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); 390 long cpuTime = 0; 391 long cpuFgTime = 0; 392 long wakelockTime = 0; 393 long gpsTime = 0; 394 if (DEBUG) Log.i(TAG, "UID " + u.getUid()); 395 if (processStats.size() > 0) { 396 // Process CPU time 397 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent 398 : processStats.entrySet()) { 399 Uid.Proc ps = ent.getValue(); 400 final long userTime = ps.getUserTime(which); 401 final long systemTime = ps.getSystemTime(which); 402 final long foregroundTime = ps.getForegroundTime(which); 403 cpuFgTime += foregroundTime * 10; // convert to millis 404 final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis 405 int totalTimeAtSpeeds = 0; 406 // Get the total first 407 for (int step = 0; step < speedSteps; step++) { 408 cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which); 409 totalTimeAtSpeeds += cpuSpeedStepTimes[step]; 410 } 411 if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1; 412 // Then compute the ratio of time spent at each speed 413 double processPower = 0; 414 for (int step = 0; step < speedSteps; step++) { 415 double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; 416 processPower += ratio * tmpCpuTime * powerCpuNormal[step]; 417 } 418 cpuTime += tmpCpuTime; 419 if (DEBUG && processPower != 0) { 420 Log.i(TAG, String.format("process %s, cpu power=%.2f", 421 ent.getKey(), processPower / 1000)); 422 } 423 power += processPower; 424 if (packageWithHighestDrain == null 425 || packageWithHighestDrain.startsWith("*")) { 426 highestDrain = processPower; 427 packageWithHighestDrain = ent.getKey(); 428 } else if (highestDrain < processPower 429 && !ent.getKey().startsWith("*")) { 430 highestDrain = processPower; 431 packageWithHighestDrain = ent.getKey(); 432 } 433 } 434 } 435 if (cpuFgTime > cpuTime) { 436 if (DEBUG && cpuFgTime > cpuTime + 10000) { 437 Log.i(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); 438 } 439 cpuTime = cpuFgTime; // Statistics may not have been gathered yet. 440 } 441 power /= 1000; 442 if (DEBUG && power != 0) Log.i(TAG, String.format("total cpu power=%.2f", power)); 443 444 // Process wake lock usage 445 Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats(); 446 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry 447 : wakelockStats.entrySet()) { 448 Uid.Wakelock wakelock = wakelockEntry.getValue(); 449 // Only care about partial wake locks since full wake locks 450 // are canceled when the user turns the screen off. 451 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); 452 if (timer != null) { 453 wakelockTime += timer.getTotalTimeLocked(uSecTime, which); 454 } 455 } 456 wakelockTime /= 1000; // convert to millis 457 appWakelockTime += wakelockTime; 458 459 // Add cost of holding a wake lock 460 p = (wakelockTime 461 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000; 462 power += p; 463 if (DEBUG && p != 0) Log.i(TAG, String.format("wakelock power=%.2f", p)); 464 465 // Add cost of mobile traffic 466 final long mobileRx = u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, mStatsType); 467 final long mobileTx = u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, mStatsType); 468 p = (mobileRx + mobileTx) * mobilePowerPerByte; 469 power += p; 470 if (DEBUG && p != 0) Log.i(TAG, String.format("mobile power=%.2f", p)); 471 472 // Add cost of wifi traffic 473 final long wifiRx = u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, mStatsType); 474 final long wifiTx = u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, mStatsType); 475 p = (wifiRx + wifiTx) * wifiPowerPerByte; 476 power += p; 477 if (DEBUG && p != 0) Log.i(TAG, String.format("wifi power=%.2f", p)); 478 479 // Add cost of keeping WIFI running. 480 long wifiRunningTimeMs = u.getWifiRunningTime(uSecTime, which) / 1000; 481 mAppWifiRunning += wifiRunningTimeMs; 482 p = (wifiRunningTimeMs 483 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000; 484 power += p; 485 if (DEBUG && p != 0) Log.i(TAG, String.format("wifi running power=%.2f", p)); 486 487 // Add cost of WIFI scans 488 long wifiScanTimeMs = u.getWifiScanTime(uSecTime, which) / 1000; 489 p = (wifiScanTimeMs 490 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / 1000; 491 power += p; 492 if (DEBUG && p != 0) Log.i(TAG, String.format("wifi scanning power=%.2f", p)); 493 for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { 494 long batchScanTimeMs = u.getWifiBatchedScanTime(bin, uSecTime, which) / 1000; 495 p = (batchScanTimeMs 496 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin)); 497 power += p; 498 if (DEBUG && p != 0) { 499 Log.i(TAG, String.format("wifi batched scanning lvl %d = %.2f", bin, p)); 500 } 501 } 502 503 // Process Sensor usage 504 Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); 505 for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry 506 : sensorStats.entrySet()) { 507 Uid.Sensor sensor = sensorEntry.getValue(); 508 int sensorHandle = sensor.getHandle(); 509 BatteryStats.Timer timer = sensor.getSensorTime(); 510 long sensorTime = timer.getTotalTimeLocked(uSecTime, which) / 1000; 511 double multiplier = 0; 512 switch (sensorHandle) { 513 case Uid.Sensor.GPS: 514 multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON); 515 gpsTime = sensorTime; 516 break; 517 default: 518 List<Sensor> sensorList = sensorManager.getSensorList( 519 android.hardware.Sensor.TYPE_ALL); 520 for (android.hardware.Sensor s : sensorList) { 521 if (s.getHandle() == sensorHandle) { 522 multiplier = s.getPower(); 523 break; 524 } 525 } 526 } 527 p = (multiplier * sensorTime) / 1000; 528 power += p; 529 if (DEBUG && p != 0) { 530 Log.i(TAG, String.format("sensor %s power=%.2f", sensor.toString(), p)); 531 } 532 } 533 534 if (DEBUG) Log.i(TAG, String.format("UID %d total power=%.2f", u.getUid(), power)); 535 536 // Add the app to the list if it is consuming power 537 boolean isOtherUser = false; 538 final int userId = UserHandle.getUserId(u.getUid()); 539 if (power != 0 || includeZeroConsumption || u.getUid() == 0) { 540 BatterySipper app = new BatterySipper(mActivity, mRequestQueue, mHandler, 541 packageWithHighestDrain, DrainType.APP, 0, u, 542 new double[] {power}); 543 app.cpuTime = cpuTime; 544 app.gpsTime = gpsTime; 545 app.wifiRunningTime = wifiRunningTimeMs; 546 app.cpuFgTime = cpuFgTime; 547 app.wakeLockTime = wakelockTime; 548 app.mobileRxBytes = mobileRx; 549 app.mobileTxBytes = mobileTx; 550 app.wifiRxBytes = wifiRx; 551 app.wifiTxBytes = wifiTx; 552 if (u.getUid() == Process.WIFI_UID) { 553 mWifiSippers.add(app); 554 } else if (u.getUid() == Process.BLUETOOTH_UID) { 555 mBluetoothSippers.add(app); 556 } else if (userId != UserHandle.myUserId() 557 && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) { 558 isOtherUser = true; 559 List<BatterySipper> list = mUserSippers.get(userId); 560 if (list == null) { 561 list = new ArrayList<BatterySipper>(); 562 mUserSippers.put(userId, list); 563 } 564 list.add(app); 565 } else { 566 mUsageList.add(app); 567 } 568 if (u.getUid() == 0) { 569 osApp = app; 570 } 571 } 572 if (power != 0 || includeZeroConsumption) { 573 if (u.getUid() == Process.WIFI_UID) { 574 mWifiPower += power; 575 } else if (u.getUid() == Process.BLUETOOTH_UID) { 576 mBluetoothPower += power; 577 } else if (isOtherUser) { 578 Double userPower = mUserPower.get(userId); 579 if (userPower == null) { 580 userPower = power; 581 } else { 582 userPower += power; 583 } 584 mUserPower.put(userId, userPower); 585 } else { 586 if (power > mMaxPower) mMaxPower = power; 587 mTotalPower += power; 588 } 589 } 590 } 591 592 // The device has probably been awake for longer than the screen on 593 // time and application wake lock time would account for. Assign 594 // this remainder to the OS, if possible. 595 if (osApp != null) { 596 long wakeTimeMillis = mStats.computeBatteryUptime( 597 SystemClock.uptimeMillis() * 1000, which) / 1000; 598 wakeTimeMillis -= appWakelockTime + (mStats.getScreenOnTime( 599 SystemClock.elapsedRealtime(), which) / 1000); 600 if (wakeTimeMillis > 0) { 601 double power = (wakeTimeMillis 602 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000; 603 if (DEBUG) Log.i(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + power); 604 osApp.wakeLockTime += wakeTimeMillis; 605 osApp.value += power; 606 osApp.values[0] += power; 607 if (osApp.value > mMaxPower) mMaxPower = osApp.value; 608 mTotalPower += power; 609 } 610 } 611 } 612 613 private void addPhoneUsage(long uSecNow) { 614 long phoneOnTimeMs = mStats.getPhoneOnTime(uSecNow, mStatsType) / 1000; 615 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 616 * phoneOnTimeMs / 1000; 617 addEntry(mActivity.getString(R.string.power_phone), DrainType.PHONE, phoneOnTimeMs, 618 R.drawable.ic_settings_voice_calls, phoneOnPower); 619 } 620 621 private void addScreenUsage(long uSecNow) { 622 double power = 0; 623 long screenOnTimeMs = mStats.getScreenOnTime(uSecNow, mStatsType) / 1000; 624 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); 625 final double screenFullPower = 626 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); 627 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { 628 double screenBinPower = screenFullPower * (i + 0.5f) 629 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 630 long brightnessTime = mStats.getScreenBrightnessTime(i, uSecNow, mStatsType) / 1000; 631 power += screenBinPower * brightnessTime; 632 if (DEBUG) { 633 Log.i(TAG, "Screen bin power = " + (int) screenBinPower + ", time = " 634 + brightnessTime); 635 } 636 } 637 power /= 1000; // To seconds 638 addEntry(mActivity.getString(R.string.power_screen), DrainType.SCREEN, screenOnTimeMs, 639 R.drawable.ic_settings_display, power); 640 } 641 642 private void addRadioUsage(long uSecNow) { 643 double power = 0; 644 final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; 645 long signalTimeMs = 0; 646 for (int i = 0; i < BINS; i++) { 647 long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, uSecNow, mStatsType) / 1000; 648 power += strengthTimeMs / 1000 649 * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); 650 signalTimeMs += strengthTimeMs; 651 } 652 long scanningTimeMs = mStats.getPhoneSignalScanningTime(uSecNow, mStatsType) / 1000; 653 power += scanningTimeMs / 1000 * mPowerProfile.getAveragePower( 654 PowerProfile.POWER_RADIO_SCANNING); 655 BatterySipper bs = 656 addEntry(mActivity.getString(R.string.power_cell), DrainType.CELL, 657 signalTimeMs, R.drawable.ic_settings_cell_standby, power); 658 if (signalTimeMs != 0) { 659 bs.noCoveragePercent = mStats.getPhoneSignalStrengthTime(0, uSecNow, mStatsType) 660 / 1000 * 100.0 / signalTimeMs; 661 } 662 } 663 664 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { 665 for (int i=0; i<from.size(); i++) { 666 BatterySipper wbs = from.get(i); 667 if (DEBUG) Log.i(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime); 668 bs.cpuTime += wbs.cpuTime; 669 bs.gpsTime += wbs.gpsTime; 670 bs.wifiRunningTime += wbs.wifiRunningTime; 671 bs.cpuFgTime += wbs.cpuFgTime; 672 bs.wakeLockTime += wbs.wakeLockTime; 673 bs.mobileRxBytes += wbs.mobileRxBytes; 674 bs.mobileTxBytes += wbs.mobileTxBytes; 675 bs.wifiRxBytes += wbs.wifiRxBytes; 676 bs.wifiTxBytes += wbs.wifiTxBytes; 677 } 678 } 679 680 private void addWiFiUsage(long uSecNow) { 681 long onTimeMs = mStats.getWifiOnTime(uSecNow, mStatsType) / 1000; 682 long runningTimeMs = mStats.getGlobalWifiRunningTime(uSecNow, mStatsType) / 1000; 683 if (DEBUG) Log.i(TAG, "WIFI runningTime=" + runningTimeMs 684 + " app runningTime=" + mAppWifiRunning); 685 runningTimeMs -= mAppWifiRunning; 686 if (runningTimeMs < 0) runningTimeMs = 0; 687 double wifiPower = (onTimeMs * 0 /* TODO */ 688 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON) 689 + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000; 690 if (DEBUG) Log.i(TAG, "WIFI power=" + wifiPower + " from procs=" + mWifiPower); 691 BatterySipper bs = addEntry(mActivity.getString(R.string.power_wifi), DrainType.WIFI, 692 runningTimeMs, R.drawable.ic_settings_wifi, wifiPower + mWifiPower); 693 aggregateSippers(bs, mWifiSippers, "WIFI"); 694 } 695 696 private void addIdleUsage(long uSecNow) { 697 long idleTimeMs = (uSecNow - mStats.getScreenOnTime(uSecNow, mStatsType)) / 1000; 698 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) 699 / 1000; 700 addEntry(mActivity.getString(R.string.power_idle), DrainType.IDLE, idleTimeMs, 701 R.drawable.ic_settings_phone_idle, idlePower); 702 } 703 704 private void addBluetoothUsage(long uSecNow) { 705 long btOnTimeMs = mStats.getBluetoothOnTime(uSecNow, mStatsType) / 1000; 706 double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON) 707 / 1000; 708 int btPingCount = mStats.getBluetoothPingCount(); 709 btPower += (btPingCount 710 * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) / 1000; 711 BatterySipper bs = addEntry(mActivity.getString(R.string.power_bluetooth), 712 DrainType.BLUETOOTH, btOnTimeMs, R.drawable.ic_settings_bluetooth, 713 btPower + mBluetoothPower); 714 aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); 715 } 716 717 private void addUserUsage() { 718 for (int i=0; i<mUserSippers.size(); i++) { 719 final int userId = mUserSippers.keyAt(i); 720 final List<BatterySipper> sippers = mUserSippers.valueAt(i); 721 UserInfo info = mUm.getUserInfo(userId); 722 Drawable icon; 723 String name; 724 if (info != null) { 725 icon = UserUtils.getUserIcon(mActivity, mUm, info, mActivity.getResources()); 726 name = info != null ? info.name : null; 727 if (name == null) { 728 name = Integer.toString(info.id); 729 } 730 name = mActivity.getResources().getString( 731 R.string.running_process_item_user_label, name); 732 } else { 733 icon = null; 734 name = mActivity.getResources().getString( 735 R.string.running_process_item_removed_user_label); 736 } 737 Double userPower = mUserPower.get(userId); 738 double power = (userPower != null) ? userPower : 0.0; 739 BatterySipper bs = addEntry(name, DrainType.USER, 0, 0, power); 740 bs.icon = icon; 741 aggregateSippers(bs, sippers, "User"); 742 } 743 } 744 745 /** 746 * Return estimated power (in mAs) of sending a byte with the mobile radio. 747 */ 748 private double getMobilePowerPerByte() { 749 final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system 750 final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 751 / 3600; 752 753 final long mobileRx = mStats.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, mStatsType); 754 final long mobileTx = mStats.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, mStatsType); 755 final long mobileData = mobileRx + mobileTx; 756 757 final long radioDataUptimeMs = mStats.getRadioDataUptime() / 1000; 758 final long mobileBps = radioDataUptimeMs != 0 759 ? mobileData * 8 * 1000 / radioDataUptimeMs 760 : MOBILE_BPS; 761 762 return MOBILE_POWER / (mobileBps / 8); 763 } 764 765 /** 766 * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio. 767 */ 768 private double getWifiPowerPerByte() { 769 final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system 770 final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) 771 / 3600; 772 return WIFI_POWER / (WIFI_BPS / 8); 773 } 774 775 private void processMiscUsage() { 776 final int which = mStatsType; 777 long uSecTime = SystemClock.elapsedRealtime() * 1000; 778 final long uSecNow = mStats.computeBatteryRealtime(uSecTime, which); 779 final long timeSinceUnplugged = uSecNow; 780 if (DEBUG) { 781 Log.i(TAG, "Uptime since last unplugged = " + (timeSinceUnplugged / 1000)); 782 } 783 784 addUserUsage(); 785 addPhoneUsage(uSecNow); 786 addScreenUsage(uSecNow); 787 addWiFiUsage(uSecNow); 788 addBluetoothUsage(uSecNow); 789 addIdleUsage(uSecNow); // Not including cellular idle power 790 // Don't compute radio usage if it's a wifi-only device 791 if (!com.android.settings.Utils.isWifiOnly(mActivity)) { 792 addRadioUsage(uSecNow); 793 } 794 } 795 796 private BatterySipper addEntry(String label, DrainType drainType, long time, int iconId, 797 double power) { 798 if (power > mMaxPower) mMaxPower = power; 799 mTotalPower += power; 800 BatterySipper bs = new BatterySipper(mActivity, mRequestQueue, mHandler, 801 label, drainType, iconId, null, new double[] {power}); 802 bs.usageTime = time; 803 bs.iconId = iconId; 804 mUsageList.add(bs); 805 return bs; 806 } 807 808 public List<BatterySipper> getUsageList() { 809 return mUsageList; 810 } 811 812 static final int MSG_UPDATE_NAME_ICON = 1; 813 static final int MSG_REPORT_FULLY_DRAWN = 2; 814 815 public double getMaxPower() { 816 return mMaxPower; 817 } 818 819 public double getTotalPower() { 820 return mTotalPower; 821 } 822 823 private void load() { 824 try { 825 byte[] data = mBatteryInfo.getStatistics(); 826 Parcel parcel = Parcel.obtain(); 827 parcel.unmarshall(data, 0, data.length); 828 parcel.setDataPosition(0); 829 mStats = com.android.internal.os.BatteryStatsImpl.CREATOR 830 .createFromParcel(parcel); 831 mStats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); 832 } catch (RemoteException e) { 833 Log.e(TAG, "RemoteException:", e); 834 } 835 } 836 } 837