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