BatteryStatsHelper.java revision d45665bf0b26fddf5716a0fd43036848d9301960
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.internal.os; 18 19import static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA; 20import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA; 21import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA; 22import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA; 23 24import android.content.Context; 25import android.hardware.Sensor; 26import android.hardware.SensorManager; 27import android.net.ConnectivityManager; 28import android.os.BatteryStats; 29import android.os.BatteryStats.Uid; 30import android.os.Bundle; 31import android.os.Parcel; 32import android.os.Process; 33import android.os.RemoteException; 34import android.os.ServiceManager; 35import android.os.SystemClock; 36import android.os.UserHandle; 37import android.telephony.SignalStrength; 38import android.util.Log; 39import android.util.SparseArray; 40 41import com.android.internal.app.IBatteryStats; 42import com.android.internal.os.BatterySipper.DrainType; 43 44import java.util.ArrayList; 45import java.util.Collections; 46import java.util.Comparator; 47import java.util.List; 48import java.util.Map; 49 50/** 51 * A helper class for retrieving the power usage information for all applications and services. 52 * 53 * The caller must initialize this class as soon as activity object is ready to use (for example, in 54 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). 55 */ 56public class BatteryStatsHelper { 57 58 private static final boolean DEBUG = false; 59 60 private static final String TAG = BatteryStatsHelper.class.getSimpleName(); 61 62 private static BatteryStats sStatsXfer; 63 64 final private Context mContext; 65 66 private IBatteryStats mBatteryInfo; 67 private BatteryStats mStats; 68 private PowerProfile mPowerProfile; 69 70 private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>(); 71 private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>(); 72 private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>(); 73 private final SparseArray<List<BatterySipper>> mUserSippers 74 = new SparseArray<List<BatterySipper>>(); 75 private final SparseArray<Double> mUserPower = new SparseArray<Double>(); 76 77 private final List<BatterySipper> mMobilemsppList = new ArrayList<BatterySipper>(); 78 79 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; 80 private int mAsUser = 0; 81 82 long mBatteryRealtime; 83 long mBatteryUptime; 84 long mTypeBatteryRealtime; 85 long mTypeBatteryUptime; 86 87 private long mStatsPeriod = 0; 88 private double mMaxPower = 1; 89 private double mComputedPower; 90 private double mTotalPower; 91 private double mWifiPower; 92 private double mBluetoothPower; 93 private double mMinDrainedPower; 94 private double mMaxDrainedPower; 95 96 // How much the apps together have kept the mobile radio active. 97 private long mAppMobileActive; 98 99 // How much the apps together have left WIFI running. 100 private long mAppWifiRunning; 101 102 public BatteryStatsHelper(Context context) { 103 mContext = context; 104 } 105 106 /** Clears the current stats and forces recreating for future use. */ 107 public void clearStats() { 108 mStats = null; 109 } 110 111 public BatteryStats getStats() { 112 if (mStats == null) { 113 load(); 114 } 115 return mStats; 116 } 117 118 public PowerProfile getPowerProfile() { 119 return mPowerProfile; 120 } 121 122 public void create(BatteryStats stats) { 123 mPowerProfile = new PowerProfile(mContext); 124 mStats = stats; 125 } 126 127 public void create(Bundle icicle) { 128 if (icicle != null) { 129 mStats = sStatsXfer; 130 } 131 mBatteryInfo = IBatteryStats.Stub.asInterface( 132 ServiceManager.getService(BatteryStats.SERVICE_NAME)); 133 mPowerProfile = new PowerProfile(mContext); 134 } 135 136 public void storeState() { 137 sStatsXfer = mStats; 138 } 139 140 public static String makemAh(double power) { 141 if (power < .00001) return String.format("%.8f", power); 142 else if (power < .0001) return String.format("%.7f", power); 143 else if (power < .001) return String.format("%.6f", power); 144 else if (power < .01) return String.format("%.5f", power); 145 else if (power < .1) return String.format("%.4f", power); 146 else if (power < 1) return String.format("%.3f", power); 147 else if (power < 10) return String.format("%.2f", power); 148 else if (power < 100) return String.format("%.1f", power); 149 else return String.format("%.0f", power); 150 } 151 152 /** 153 * Refreshes the power usage list. 154 */ 155 public void refreshStats(int statsType, int asUser) { 156 refreshStats(statsType, asUser, SystemClock.elapsedRealtime() * 1000, 157 SystemClock.uptimeMillis() * 1000); 158 } 159 160 public void refreshStats(int statsType, int asUser, long rawRealtimeNano, long rawUptimeNano) { 161 // Initialize mStats if necessary. 162 getStats(); 163 164 mMaxPower = 0; 165 mComputedPower = 0; 166 mTotalPower = 0; 167 mWifiPower = 0; 168 mBluetoothPower = 0; 169 mAppMobileActive = 0; 170 mAppWifiRunning = 0; 171 172 mUsageList.clear(); 173 mWifiSippers.clear(); 174 mBluetoothSippers.clear(); 175 mUserSippers.clear(); 176 mUserPower.clear(); 177 mMobilemsppList.clear(); 178 179 if (mStats == null) { 180 return; 181 } 182 183 mStatsType = statsType; 184 mAsUser = asUser; 185 mBatteryUptime = mStats.getBatteryUptime(rawUptimeNano); 186 mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeNano); 187 mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeNano, mStatsType); 188 mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeNano, mStatsType); 189 190 if (DEBUG) { 191 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeNano/1000) + " uptime=" 192 + (rawUptimeNano/1000)); 193 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime=" 194 + (mBatteryUptime/1000)); 195 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime=" 196 + (mTypeBatteryUptime/1000)); 197 } 198 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() 199 * mPowerProfile.getBatteryCapacity()) / 100; 200 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() 201 * mPowerProfile.getBatteryCapacity()) / 100; 202 203 processAppUsage(); 204 205 // Before aggregating apps in to users, collect all apps to sort by their ms per packet. 206 for (int i=0; i<mUsageList.size(); i++) { 207 BatterySipper bs = mUsageList.get(i); 208 bs.computeMobilemspp(); 209 if (bs.mobilemspp != 0) { 210 mMobilemsppList.add(bs); 211 } 212 } 213 for (int i=0; i<mUserSippers.size(); i++) { 214 List<BatterySipper> user = mUserSippers.valueAt(i); 215 for (int j=0; j<user.size(); j++) { 216 BatterySipper bs = user.get(j); 217 bs.computeMobilemspp(); 218 if (bs.mobilemspp != 0) { 219 mMobilemsppList.add(bs); 220 } 221 } 222 } 223 Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() { 224 @Override 225 public int compare(BatterySipper lhs, BatterySipper rhs) { 226 if (lhs.mobilemspp < rhs.mobilemspp) { 227 return 1; 228 } else if (lhs.mobilemspp > rhs.mobilemspp) { 229 return -1; 230 } 231 return 0; 232 } 233 }); 234 235 processMiscUsage(); 236 237 if (DEBUG) { 238 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge=" 239 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower)); 240 } 241 mTotalPower = mComputedPower; 242 if (mStats.getLowDischargeAmountSinceCharge() > 1) { 243 if (mMinDrainedPower > mComputedPower) { 244 double amount = mMinDrainedPower - mComputedPower; 245 mTotalPower = mMinDrainedPower; 246 addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount); 247 } else if (mMaxDrainedPower < mComputedPower) { 248 double amount = mComputedPower - mMaxDrainedPower; 249 addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount); 250 } 251 } 252 253 Collections.sort(mUsageList); 254 } 255 256 private void processAppUsage() { 257 SensorManager sensorManager = (SensorManager) mContext.getSystemService( 258 Context.SENSOR_SERVICE); 259 final int which = mStatsType; 260 final int speedSteps = mPowerProfile.getNumSpeedSteps(); 261 final double[] powerCpuNormal = new double[speedSteps]; 262 final long[] cpuSpeedStepTimes = new long[speedSteps]; 263 for (int p = 0; p < speedSteps; p++) { 264 powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); 265 } 266 final double mobilePowerPerPacket = getMobilePowerPerPacket(); 267 final double mobilePowerPerMs = getMobilePowerPerMs(); 268 final double wifiPowerPerPacket = getWifiPowerPerPacket(); 269 long appWakelockTime = 0; 270 BatterySipper osApp = null; 271 mStatsPeriod = mTypeBatteryRealtime; 272 SparseArray<? extends Uid> uidStats = mStats.getUidStats(); 273 final int NU = uidStats.size(); 274 for (int iu = 0; iu < NU; iu++) { 275 Uid u = uidStats.valueAt(iu); 276 double p; // in mAs 277 double power = 0; // in mAs 278 double highestDrain = 0; 279 String packageWithHighestDrain = null; 280 Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); 281 long cpuTime = 0; 282 long cpuFgTime = 0; 283 long wakelockTime = 0; 284 long gpsTime = 0; 285 if (processStats.size() > 0) { 286 // Process CPU time 287 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent 288 : processStats.entrySet()) { 289 Uid.Proc ps = ent.getValue(); 290 final long userTime = ps.getUserTime(which); 291 final long systemTime = ps.getSystemTime(which); 292 final long foregroundTime = ps.getForegroundTime(which); 293 cpuFgTime += foregroundTime * 10; // convert to millis 294 final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis 295 int totalTimeAtSpeeds = 0; 296 // Get the total first 297 for (int step = 0; step < speedSteps; step++) { 298 cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which); 299 totalTimeAtSpeeds += cpuSpeedStepTimes[step]; 300 } 301 if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1; 302 // Then compute the ratio of time spent at each speed 303 double processPower = 0; 304 for (int step = 0; step < speedSteps; step++) { 305 double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; 306 if (DEBUG && ratio != 0) Log.d(TAG, "UID " + u.getUid() + ": CPU step #" 307 + step + " ratio=" + makemAh(ratio) + " power=" 308 + makemAh(ratio*tmpCpuTime*powerCpuNormal[step] / (60*60*1000))); 309 processPower += ratio * tmpCpuTime * powerCpuNormal[step]; 310 } 311 cpuTime += tmpCpuTime; 312 if (DEBUG && processPower != 0) { 313 Log.d(TAG, String.format("process %s, cpu power=%s", 314 ent.getKey(), makemAh(processPower / (60*60*1000)))); 315 } 316 power += processPower; 317 if (packageWithHighestDrain == null 318 || packageWithHighestDrain.startsWith("*")) { 319 highestDrain = processPower; 320 packageWithHighestDrain = ent.getKey(); 321 } else if (highestDrain < processPower 322 && !ent.getKey().startsWith("*")) { 323 highestDrain = processPower; 324 packageWithHighestDrain = ent.getKey(); 325 } 326 } 327 } 328 if (cpuFgTime > cpuTime) { 329 if (DEBUG && cpuFgTime > cpuTime + 10000) { 330 Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); 331 } 332 cpuTime = cpuFgTime; // Statistics may not have been gathered yet. 333 } 334 power /= (60*60*1000); 335 336 // Process wake lock usage 337 Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats(); 338 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry 339 : wakelockStats.entrySet()) { 340 Uid.Wakelock wakelock = wakelockEntry.getValue(); 341 // Only care about partial wake locks since full wake locks 342 // are canceled when the user turns the screen off. 343 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); 344 if (timer != null) { 345 wakelockTime += timer.getTotalTimeLocked(mBatteryRealtime, which); 346 } 347 } 348 wakelockTime /= 1000; // convert to millis 349 appWakelockTime += wakelockTime; 350 351 // Add cost of holding a wake lock 352 p = (wakelockTime 353 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000); 354 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake " 355 + wakelockTime + " power=" + makemAh(p)); 356 power += p; 357 358 // Add cost of mobile traffic 359 final long mobileRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType); 360 final long mobileTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType); 361 final long mobileRxB = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType); 362 final long mobileTxB = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType); 363 final long mobileActive = u.getMobileRadioActiveTime(mStatsType); 364 if (mobileActive > 0) { 365 // We are tracking when the radio is up, so can use the active time to 366 // determine power use. 367 mAppMobileActive += mobileActive; 368 p = (mobilePowerPerMs * mobileActive) / 1000; 369 } else { 370 // We are not tracking when the radio is up, so must approximate power use 371 // based on the number of packets. 372 p = (mobileRx + mobileTx) * mobilePowerPerPacket; 373 } 374 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets " 375 + (mobileRx+mobileTx) + " active time " + mobileActive 376 + " power=" + makemAh(p)); 377 power += p; 378 379 // Add cost of wifi traffic 380 final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType); 381 final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType); 382 final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType); 383 final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType); 384 p = (wifiRx + wifiTx) * wifiPowerPerPacket; 385 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets " 386 + (mobileRx+mobileTx) + " power=" + makemAh(p)); 387 power += p; 388 389 // Add cost of keeping WIFI running. 390 long wifiRunningTimeMs = u.getWifiRunningTime(mBatteryRealtime, which) / 1000; 391 mAppWifiRunning += wifiRunningTimeMs; 392 p = (wifiRunningTimeMs 393 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000); 394 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running " 395 + wifiRunningTimeMs + " power=" + makemAh(p)); 396 power += p; 397 398 // Add cost of WIFI scans 399 long wifiScanTimeMs = u.getWifiScanTime(mBatteryRealtime, which) / 1000; 400 p = (wifiScanTimeMs 401 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000); 402 if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs 403 + " power=" + makemAh(p)); 404 power += p; 405 for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { 406 long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mBatteryRealtime, which) / 1000; 407 p = ((batchScanTimeMs 408 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin)) 409 ) / (60*60*1000); 410 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin 411 + " time=" + batchScanTimeMs + " power=" + makemAh(p)); 412 power += p; 413 } 414 415 // Process Sensor usage 416 Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); 417 for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry 418 : sensorStats.entrySet()) { 419 Uid.Sensor sensor = sensorEntry.getValue(); 420 int sensorHandle = sensor.getHandle(); 421 BatteryStats.Timer timer = sensor.getSensorTime(); 422 long sensorTime = timer.getTotalTimeLocked(mBatteryRealtime, which) / 1000; 423 double multiplier = 0; 424 switch (sensorHandle) { 425 case Uid.Sensor.GPS: 426 multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON); 427 gpsTime = sensorTime; 428 break; 429 default: 430 List<Sensor> sensorList = sensorManager.getSensorList( 431 android.hardware.Sensor.TYPE_ALL); 432 for (android.hardware.Sensor s : sensorList) { 433 if (s.getHandle() == sensorHandle) { 434 multiplier = s.getPower(); 435 break; 436 } 437 } 438 } 439 p = (multiplier * sensorTime) / (60*60*1000); 440 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle 441 + " time=" + sensorTime + " power=" + makemAh(p)); 442 power += p; 443 } 444 445 if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s", 446 u.getUid(), makemAh(power))); 447 448 // Add the app to the list if it is consuming power 449 final int userId = UserHandle.getUserId(u.getUid()); 450 if (power != 0 || u.getUid() == 0) { 451 BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 452 new double[] {power}); 453 app.cpuTime = cpuTime; 454 app.gpsTime = gpsTime; 455 app.wifiRunningTime = wifiRunningTimeMs; 456 app.cpuFgTime = cpuFgTime; 457 app.wakeLockTime = wakelockTime; 458 app.mobileRxPackets = mobileRx; 459 app.mobileTxPackets = mobileTx; 460 app.mobileActive = mobileActive / 1000; 461 app.wifiRxPackets = wifiRx; 462 app.wifiTxPackets = wifiTx; 463 app.mobileRxBytes = mobileRxB; 464 app.mobileTxBytes = mobileTxB; 465 app.wifiRxBytes = wifiRxB; 466 app.wifiTxBytes = wifiTxB; 467 app.packageWithHighestDrain = packageWithHighestDrain; 468 if (u.getUid() == Process.WIFI_UID) { 469 mWifiSippers.add(app); 470 mWifiPower += power; 471 } else if (u.getUid() == Process.BLUETOOTH_UID) { 472 mBluetoothSippers.add(app); 473 mBluetoothPower += power; 474 } else if (mAsUser != UserHandle.USER_ALL && userId != mAsUser 475 && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) { 476 List<BatterySipper> list = mUserSippers.get(userId); 477 if (list == null) { 478 list = new ArrayList<BatterySipper>(); 479 mUserSippers.put(userId, list); 480 } 481 list.add(app); 482 if (power != 0) { 483 Double userPower = mUserPower.get(userId); 484 if (userPower == null) { 485 userPower = power; 486 } else { 487 userPower += power; 488 } 489 mUserPower.put(userId, userPower); 490 } 491 } else { 492 mUsageList.add(app); 493 if (power > mMaxPower) mMaxPower = power; 494 mComputedPower += power; 495 } 496 if (u.getUid() == 0) { 497 osApp = app; 498 } 499 } 500 } 501 502 // The device has probably been awake for longer than the screen on 503 // time and application wake lock time would account for. Assign 504 // this remainder to the OS, if possible. 505 if (osApp != null) { 506 long wakeTimeMillis = mBatteryUptime / 1000; 507 wakeTimeMillis -= appWakelockTime 508 + (mStats.getScreenOnTime(mBatteryRealtime, which) / 1000); 509 if (wakeTimeMillis > 0) { 510 double power = (wakeTimeMillis 511 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) 512 / (60*60*1000); 513 if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " 514 + makemAh(power)); 515 osApp.wakeLockTime += wakeTimeMillis; 516 osApp.value += power; 517 osApp.values[0] += power; 518 if (osApp.value > mMaxPower) mMaxPower = osApp.value; 519 mComputedPower += power; 520 } 521 } 522 } 523 524 private void addPhoneUsage() { 525 long phoneOnTimeMs = mStats.getPhoneOnTime(mBatteryRealtime, mStatsType) / 1000; 526 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 527 * phoneOnTimeMs / (60*60*1000); 528 if (phoneOnPower != 0) { 529 BatterySipper bs = addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower); 530 } 531 } 532 533 private void addScreenUsage() { 534 double power = 0; 535 long screenOnTimeMs = mStats.getScreenOnTime(mBatteryRealtime, mStatsType) / 1000; 536 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); 537 final double screenFullPower = 538 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); 539 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { 540 double screenBinPower = screenFullPower * (i + 0.5f) 541 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; 542 long brightnessTime = mStats.getScreenBrightnessTime(i, mBatteryRealtime, mStatsType) 543 / 1000; 544 double p = screenBinPower*brightnessTime; 545 if (DEBUG && p != 0) { 546 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime 547 + " power=" + makemAh(p / (60 * 60 * 1000))); 548 } 549 power += p; 550 } 551 power /= (60*60*1000); // To hours 552 if (power != 0) { 553 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power); 554 } 555 } 556 557 private void addRadioUsage() { 558 double power = 0; 559 final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; 560 long signalTimeMs = 0; 561 long noCoverageTimeMs = 0; 562 for (int i = 0; i < BINS; i++) { 563 long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mBatteryRealtime, mStatsType) 564 / 1000; 565 double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i)) 566 / (60*60*1000); 567 if (DEBUG && p != 0) { 568 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" 569 + makemAh(p)); 570 } 571 power += p; 572 signalTimeMs += strengthTimeMs; 573 if (i == 0) { 574 noCoverageTimeMs = strengthTimeMs; 575 } 576 } 577 long scanningTimeMs = mStats.getPhoneSignalScanningTime(mBatteryRealtime, mStatsType) 578 / 1000; 579 double p = (scanningTimeMs * mPowerProfile.getAveragePower( 580 PowerProfile.POWER_RADIO_SCANNING)) 581 / (60*60*1000); 582 if (DEBUG && p != 0) { 583 Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p)); 584 } 585 power += p; 586 long radioActiveTimeUs = mStats.getMobileRadioActiveTime(mBatteryRealtime, mStatsType); 587 long remainingActiveTime = (radioActiveTimeUs - mAppMobileActive) / 1000; 588 if (remainingActiveTime > 0) { 589 power += getMobilePowerPerMs() * remainingActiveTime; 590 } 591 if (power != 0) { 592 BatterySipper bs = 593 addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power); 594 if (signalTimeMs != 0) { 595 bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs; 596 } 597 bs.mobileActive = remainingActiveTime; 598 } 599 } 600 601 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { 602 for (int i=0; i<from.size(); i++) { 603 BatterySipper wbs = from.get(i); 604 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime); 605 bs.cpuTime += wbs.cpuTime; 606 bs.gpsTime += wbs.gpsTime; 607 bs.wifiRunningTime += wbs.wifiRunningTime; 608 bs.cpuFgTime += wbs.cpuFgTime; 609 bs.wakeLockTime += wbs.wakeLockTime; 610 bs.mobileRxPackets += wbs.mobileRxPackets; 611 bs.mobileTxPackets += wbs.mobileTxPackets; 612 bs.mobileActive += wbs.mobileActive; 613 bs.wifiRxPackets += wbs.wifiRxPackets; 614 bs.wifiTxPackets += wbs.wifiTxPackets; 615 bs.mobileRxBytes += wbs.mobileRxBytes; 616 bs.mobileTxBytes += wbs.mobileTxBytes; 617 bs.wifiRxBytes += wbs.wifiRxBytes; 618 bs.wifiTxBytes += wbs.wifiTxBytes; 619 } 620 bs.computeMobilemspp(); 621 } 622 623 private void addWiFiUsage() { 624 long onTimeMs = mStats.getWifiOnTime(mBatteryRealtime, mStatsType) / 1000; 625 long runningTimeMs = mStats.getGlobalWifiRunningTime(mBatteryRealtime, mStatsType) / 1000; 626 if (DEBUG) Log.d(TAG, "WIFI runningTime=" + runningTimeMs 627 + " app runningTime=" + mAppWifiRunning); 628 runningTimeMs -= mAppWifiRunning; 629 if (runningTimeMs < 0) runningTimeMs = 0; 630 double wifiPower = (onTimeMs * 0 /* TODO */ 631 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON) 632 + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) 633 / (60*60*1000); 634 if (DEBUG && wifiPower != 0) { 635 Log.d(TAG, "Wifi: time=" + runningTimeMs + " power=" + makemAh(wifiPower)); 636 } 637 if ((wifiPower+mWifiPower) != 0) { 638 BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, runningTimeMs, 639 wifiPower + mWifiPower); 640 aggregateSippers(bs, mWifiSippers, "WIFI"); 641 } 642 } 643 644 private void addIdleUsage() { 645 long idleTimeMs = (mTypeBatteryRealtime 646 - mStats.getScreenOnTime(mBatteryRealtime, mStatsType)) / 1000; 647 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) 648 / (60*60*1000); 649 if (DEBUG && idlePower != 0) { 650 Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower)); 651 } 652 if (idlePower != 0) { 653 addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower); 654 } 655 } 656 657 private void addBluetoothUsage() { 658 long btOnTimeMs = mStats.getBluetoothOnTime(mBatteryRealtime, mStatsType) / 1000; 659 double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON) 660 / (60*60*1000); 661 if (DEBUG && btPower != 0) { 662 Log.d(TAG, "Bluetooth: time=" + btOnTimeMs + " power=" + makemAh(btPower)); 663 } 664 int btPingCount = mStats.getBluetoothPingCount(); 665 double pingPower = (btPingCount 666 * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) 667 / (60*60*1000); 668 if (DEBUG && pingPower != 0) { 669 Log.d(TAG, "Bluetooth ping: count=" + btPingCount + " power=" + makemAh(pingPower)); 670 } 671 btPower += pingPower; 672 if ((btPower+mBluetoothPower) != 0) { 673 BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, btOnTimeMs, 674 btPower + mBluetoothPower); 675 aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); 676 } 677 } 678 679 private void addUserUsage() { 680 for (int i=0; i<mUserSippers.size(); i++) { 681 final int userId = mUserSippers.keyAt(i); 682 final List<BatterySipper> sippers = mUserSippers.valueAt(i); 683 Double userPower = mUserPower.get(userId); 684 double power = (userPower != null) ? userPower : 0.0; 685 BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power); 686 bs.userId = userId; 687 aggregateSippers(bs, sippers, "User"); 688 } 689 } 690 691 /** 692 * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio. 693 */ 694 private double getMobilePowerPerPacket() { 695 final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system 696 final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) 697 / 3600; 698 699 final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType); 700 final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType); 701 final long mobileData = mobileRx + mobileTx; 702 703 final long radioDataUptimeMs 704 = mStats.getMobileRadioActiveTime(mBatteryRealtime, mStatsType) / 1000; 705 final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0) 706 ? (mobileData / (double)radioDataUptimeMs) 707 : (((double)MOBILE_BPS) / 8 / 2048); 708 709 return (MOBILE_POWER / mobilePps) / (60*60); 710 } 711 712 /** 713 * Return estimated power (in mAs) of keeping the radio up 714 */ 715 private double getMobilePowerPerMs() { 716 return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) / (60*60*1000); 717 } 718 719 /** 720 * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio. 721 */ 722 private double getWifiPowerPerPacket() { 723 final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system 724 final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) 725 / 3600; 726 return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60); 727 } 728 729 private void processMiscUsage() { 730 addUserUsage(); 731 addPhoneUsage(); 732 addScreenUsage(); 733 addWiFiUsage(); 734 addBluetoothUsage(); 735 addIdleUsage(); // Not including cellular idle power 736 // Don't compute radio usage if it's a wifi-only device 737 ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( 738 Context.CONNECTIVITY_SERVICE); 739 if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) { 740 addRadioUsage(); 741 } 742 } 743 744 private BatterySipper addEntry(DrainType drainType, long time, double power) { 745 mComputedPower += power; 746 return addEntryNoTotal(drainType, time, power); 747 } 748 749 private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) { 750 if (power > mMaxPower) mMaxPower = power; 751 BatterySipper bs = new BatterySipper(drainType, null, new double[] {power}); 752 bs.usageTime = time; 753 mUsageList.add(bs); 754 return bs; 755 } 756 757 public List<BatterySipper> getUsageList() { 758 return mUsageList; 759 } 760 761 public List<BatterySipper> getMobilemsppList() { 762 return mMobilemsppList; 763 } 764 765 public long getStatsPeriod() { return mStatsPeriod; } 766 767 public int getStatsType() { return mStatsType; }; 768 769 public double getMaxPower() { return mMaxPower; } 770 771 public double getTotalPower() { return mTotalPower; } 772 773 public double getComputedPower() { return mComputedPower; } 774 775 public double getMinDrainedPower() { 776 return mMinDrainedPower; 777 } 778 779 public double getMaxDrainedPower() { 780 return mMaxDrainedPower; 781 } 782 783 private void load() { 784 if (mBatteryInfo == null) { 785 return; 786 } 787 try { 788 byte[] data = mBatteryInfo.getStatistics(); 789 Parcel parcel = Parcel.obtain(); 790 parcel.unmarshall(data, 0, data.length); 791 parcel.setDataPosition(0); 792 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR 793 .createFromParcel(parcel); 794 stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); 795 mStats = stats; 796 } catch (RemoteException e) { 797 Log.e(TAG, "RemoteException:", e); 798 } 799 } 800} 801