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