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