Android O电量统计

平台:Android O


电量统计

电池电量统计:

  • 软件电量计算:BatteryStatsHelper类中的processAppUsage()方法
  • 硬件电量计算:BatteryStatsHelper类中的processMiscUsage()方法

frameworks/base/core/java/com/android/internal/os/BatteryStatsHelper.java
framework/base/core/java/com/andoroid/internal/os/PowerProfile.java device/mediateksample/a3a64g/overlay/frameworks/base/core/res/res/xml/power_profile.xml
frameworks/base/core/java/com/android/internal/os/CpuPowerCalculator.java
frameworks/base/core/java/com/android/internal/os/WakelockPowerCalculator.java
frameworks/base/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
frameworks/base/core/java/com/android/internal/os/WifiPowerCalculator.java
frameworks/base/core/java/com/android/internal/os/WifiPowerEstimator.java
frameworks/base/core/java/com/android/internal/os/BluetoothPowerCalculator.java
frameworks/base/core/java/com/android/internal/os/SensorPowerCalculator.java
frameworks/base/core/java/com/android/internal/os/CameraPowerCalculator.java
frameworks/base/core/java/com/android/internal/os/FlashlightPowerCalculator.java


耗电类型:

  1. public enum DrainType {
  2. IDLE,
  3. CELL,
  4. PHONE,
  5. WIFI,
  6. BLUETOOTH,
  7. FLASHLIGHT,
  8. SCREEN,
  9. APP,
  10. USER,
  11. UNACCOUNTED,
  12. OVERCOUNTED,
  13. CAMERA,
  14. MEMORY
  15. }

1 软件电量统计

软件的耗电,都属于上面分类的APP类别

BatteryStatsHelper.java—>processAppUsage()

  1. private void processAppUsage(SparseArray<UserHandle> asUsers) {
  2. // 对所有用户进行统计?
  3. final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
  4. mStatsPeriod = mTypeBatteryRealtimeUs;
  5. BatterySipper osSipper = null;
  6. // 获取每一个uid的统计信息
  7. final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
  8. final int NU = uidStats.size();
  9. // 遍历每个uid的耗电
  10. for (int iu = 0; iu < NU; iu++) {
  11. final Uid u = uidStats.valueAt(iu);
  12. // 统计归属于BatterySipper.DrainType.APP类别
  13. final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
  14. mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
  15. mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
  16. mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
  17. mStatsType);
  18. mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
  19. mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
  20. mStatsType);
  21. mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
  22. mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
  23. mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
  24. mStatsType);
  25. final double totalPower = app.sumPower();// 耗电累加
  26. if (DEBUG && totalPower != 0) {
  27. Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
  28. makemAh(totalPower)));
  29. }
  30. // 此时耗电统计已经完成,只是为了将数据分类统计.
  31. // Add the app to the list if it is consuming power.
  32. if (totalPower != 0 || u.getUid() == 0) {//有耗电去统计,
  33. // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
  34. //
  35. final int uid = app.getUid();
  36. final int userId = UserHandle.getUserId(uid);
  37. if (uid == Process.WIFI_UID) {// mWifiSippers保存使用了wifi的app及其对应的功耗
  38. mWifiSippers.add(app);
  39. } else if (uid == Process.BLUETOOTH_UID) {// mBluetoothSippers保存使用了Bluetooth的app及其对应的功耗
  40. mBluetoothSippers.add(app);
  41. } else if (!forAllUsers && asUsers.get(userId) == null
  42. && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
  43. // We are told to just report this user's apps as one large entry.
  44. List<BatterySipper> list = mUserSippers.get(userId);
  45. if (list == null) {
  46. list = new ArrayList<>();
  47. mUserSippers.put(userId, list);
  48. }
  49. list.add(app);
  50. } else {
  51. mUsageList.add(app);
  52. }
  53. if (uid == 0) {// 如果这次统计的是OS的耗电,那么初始化变量osSipper
  54. osSipper = app;
  55. }
  56. }
  57. }
  58. // osSipper被初始化过,也就是上面统计的uid里面有系统的uid,下面把系统的耗电累加起来
  59. if (osSipper != null) {// 长时间cpu唤醒,但是屏幕没有亮,该部分的耗算入OS耗电
  60. // The device has probably been awake for longer than the screen on
  61. // time and application wake lock time would account for. Assign
  62. // this remainder to the OS, if possible.
  63. mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs,
  64. mRawUptimeUs, mStatsType);
  65. // OS耗电求和
  66. osSipper.sumPower();
  67. }
  68. }

其中mStatsType 定义为:

  1. private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;

表明是从上次充满电算起,实测中表明,充满电要拔掉充电线才清零开始算.
mStatsType的状态有三种,下面的定义来自BatteryStats.java

  1. /**
  2. * Include all of the data in the stats, including previously saved data.
  3. */
  4. public static final int STATS_SINCE_CHARGED = 0;
  5. /**
  6. * Include only the current run in the stats.
  7. */
  8. public static final int STATS_CURRENT = 1;
  9. /**
  10. * Include only the run since the last time the device was unplugged in the stats.
  11. */
  12. public static final int STATS_SINCE_UNPLUGGED = 2;
1.1cpu耗电计算

frameworks/base/core/java/com/android/internal/os/CpuPowerCalculator.java

  1. @Override
  2. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  3. long rawUptimeUs, int statsType) {
  4. app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
  5. // Aggregate total time spent on each cluster.
  6. long totalTime = 0;
  7. final int numClusters = mProfile.getNumCpuClusters();
  8. for (int cluster = 0; cluster < numClusters; cluster++) {
  9. final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
  10. for (int speed = 0; speed < speedsForCluster; speed++) {
  11. totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType);
  12. }
  13. }
  14. totalTime = Math.max(totalTime, 1);
  15. double cpuPowerMaMs = 0;
  16. for (int cluster = 0; cluster < numClusters; cluster++) {//cluster是指cpu的簇数量,在电池配置文件中有(1)
  17. final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
  18. for (int speed = 0; speed < speedsForCluster; speed++) {//speed值cpu的频点数(8)
  19. final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) /
  20. totalTime;
  21. final double cpuSpeedStepPower = ratio * app.cpuTimeMs *
  22. mProfile.getAveragePowerForCpu(cluster, speed);//不同cpu频率的单位耗电在电池配置文件中
  23. if (DEBUG && ratio != 0) {
  24. Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
  25. + speed + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
  26. + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
  27. }
  28. cpuPowerMaMs += cpuSpeedStepPower;
  29. }
  30. }
  31. // 保存cpu的耗电
  32. app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
  33. if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
  34. Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
  35. + BatteryStatsHelper.makemAh(app.cpuPowerMah));
  36. }
  37. // Keep track of the package with highest drain.
  38. double highestDrain = 0;
  39. app.cpuFgTimeMs = 0;
  40. final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
  41. final int processStatsCount = processStats.size();
  42. for (int i = 0; i < processStatsCount; i++) {
  43. final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
  44. final String processName = processStats.keyAt(i);
  45. app.cpuFgTimeMs += ps.getForegroundTime(statsType);
  46. final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType)
  47. + ps.getForegroundTime(statsType);
  48. // Each App can have multiple packages and with multiple running processes.
  49. // Keep track of the package who's process has the highest drain.
  50. if (app.packageWithHighestDrain == null ||
  51. app.packageWithHighestDrain.startsWith("*")) {
  52. highestDrain = costValue;
  53. app.packageWithHighestDrain = processName;
  54. } else if (highestDrain < costValue && !processName.startsWith("*")) {
  55. highestDrain = costValue;
  56. app.packageWithHighestDrain = processName;
  57. }
  58. }
  59. // Ensure that the CPU times make sense.
  60. if (app.cpuFgTimeMs > app.cpuTimeMs) {
  61. if (DEBUG && app.cpuFgTimeMs > app.cpuTimeMs + 10000) {
  62. Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
  63. }
  64. // Statistics may not have been gathered yet.
  65. app.cpuTimeMs = app.cpuFgTimeMs;
  66. }
  67. }

关于4g项目中的电池配置文件:

  1. <array name="cpu.clusters.cores">
  2. <value>4</value>
  3. </array>
  4. <array name="cpu.speeds.cluster0">
  5. <value>299000</value>
  6. <value>442000</value>
  7. <value>598000</value>
  8. <value>819000</value>
  9. <value>1040000</value>
  10. <value>1170000</value>
  11. <value>1300000</value>
  12. <value>1443000</value>
  13. </array>
  14. <array name="cpu.active.cluster0">
  15. <value>90.1</value>
  16. <value>110.2</value>
  17. <value>130.3</value>
  18. <value>160.4</value>
  19. <value>190.5</value>
  20. <value>220.6</value>
  21. <value>250.7</value>
  22. <value>280.8</value>
  23. </array>
  24. <item name="cpu.idle">4.8</item>
  25. <item name="cpu.awake">21.1</item>

cpu耗电的计算公式:
cpuPower = ratio_1 cpu_time cpu_ratio_1_power + … +ratio_n cpu_time cpu_ratio_n_power
其中: ratio_i = cpu_speed_time/ cpu_speeds_total_time,(i=1,2,…,N,N为CPU频点个数)

1.2 wakeup耗电统计

统计唤醒机制导致的耗电 WakelockPowerCalculator->calculateApp()

  1. public WakelockPowerCalculator(PowerProfile profile) {
  2. // 从电源配置文件中获取cpu.awake的值
  3. mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
  4. }
  5. // 计算app在cpu处于awake状态下的耗电量
  6. @Override
  7. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  8. long rawUptimeUs, int statsType) {
  9. long wakeLockTimeUs = 0;
  10. final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
  11. u.getWakelockStats();
  12. // 唤醒锁的状态的次数
  13. final int wakelockStatsCount = wakelockStats.size();
  14. // 将每次唤醒中以PARTIAL_WAKE_LOCK方式唤醒的时间统计出来
  15. for (int i = 0; i < wakelockStatsCount; i++) {
  16. final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i);
  17. // Only care about partial wake locks since full wake locks
  18. // are canceled when the user turns the screen off.
  19. // 只统计PARTIAL_WAKE_LOCK.长时间运行的后台服务,例如Service等
  20. BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
  21. if (timer != null) {
  22. wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
  23. }
  24. }
  25. // 时间单位转换
  26. app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis
  27. mTotalAppWakelockTimeMs += app.wakeLockTimeMs;
  28. // Add cost of holding a wake lock.
  29. // 将计算的唤醒耗电保存
  30. app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60);
  31. if (DEBUG && app.wakeLockPowerMah != 0) {
  32. Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs
  33. + " power=" + BatteryStatsHelper.makemAh(app.wakeLockPowerMah));
  34. }
  35. }
  36. @Override
  37. // 统计app之外的耗电量,长时间就是cpu被唤醒,但是屏幕没有亮的耗电,该部分的耗电会被算入OS
  38. public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
  39. long rawUptimeUs, int statsType) {
  40. long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000;// 返回当前电池正常运行时间
  41. // 电池运行时间 - PARTIAL_WAKE_LOCK占用的时间 + 设备在运行,屏幕亮着的时间
  42. wakeTimeMillis -= mTotalAppWakelockTimeMs
  43. + (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000);
  44. if (wakeTimeMillis > 0) {// 如果有未计算在内的耗电,添加到app.wakeLockPowerMah的耗电中
  45. final double power = (wakeTimeMillis * mPowerWakelock) / (1000*60*60);
  46. if (DEBUG) {
  47. Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
  48. + BatteryStatsHelper.makemAh(power));
  49. }
  50. app.wakeLockTimeMs += wakeTimeMillis;
  51. app.wakeLockPowerMah += power;
  52. }
  53. }

PowerProfile.POWER_CPU_AWAKE:

  1. <item name="cpu.awake">21.1</item>

计算公式:
app wakeLock耗电计算公式:
wakeLockPowerMah = (app.wakeLockTimeMs mPowerWakelock) / (10006060);
OS wakeLock耗电计算公式:
power = (wakeTimeMillis
mPowerWakelock) / (10006060);

其中: wakeTimeMillis是排除亮屏幕 和 app wakeLock 时间的电池总运行时间.
calculateRemaining统计的部分耗电会算入系统总耗电.

1.3 radio耗电统计

  1. /**
  2. * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
  3. */
  4. // 估计传输一个数据包(2k)的耗电
  5. private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {
  6. final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
  7. final double MOBILE_POWER = mPowerRadioOn / 3600;
  8. // 获取发送和接受的数据总量
  9. final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
  10. statsType);
  11. final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
  12. statsType);
  13. final long mobileData = mobileRx + mobileTx;
  14. // 获取cell处于radio.active状态的总时间
  15. final long radioDataUptimeMs =
  16. mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
  17. // 计算单位时间的传输数据量(发送 + 接受)
  18. final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
  19. ? (mobileData / (double)radioDataUptimeMs)
  20. : (((double)MOBILE_BPS) / 8 / 2048);
  21. return (MOBILE_POWER / mobilePps) / (60*60);
  22. }
  23. public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) {
  24. // 获取处于radio.active的单位耗电.注意:mPowerRadioOn是RADIO_ACTIVE,不是RADIO_ON!!!!
  25. mPowerRadioOn = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE);
  26. // 获取不同的信号等级的单位耗电(一般是5级,4g项目是2个,第一个代表无信号,第二个代表有信号)
  27. for (int i = 0; i < mPowerBins.length; i++) {// android会进行转换,length为6,(0-5),0代表无信号,5代表信号最强
  28. mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i);
  29. }
  30. // 获取扫描信号的单位耗电
  31. mPowerScan = profile.getAveragePower(PowerProfile.POWER_RADIO_SCANNING);
  32. mStats = stats;
  33. }
  34. // 统计app移动通信的耗电
  35. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  36. long rawUptimeUs, int statsType) {
  37. // Add cost of mobile traffic.
  38. app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
  39. statsType);
  40. app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
  41. statsType);
  42. // 获取处于radio.active状态的时间(ms)
  43. app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000;
  44. app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);
  45. app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
  46. statsType);
  47. app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
  48. statsType);
  49. if (app.mobileActive > 0) {// 如果统计到了处于radio.active状态的时间,用时间进行计算
  50. // We are tracking when the radio is up, so can use the active time to
  51. // determine power use.
  52. mTotalAppMobileActiveMs += app.mobileActive;
  53. app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60);
  54. } else {// 如果没有统计到时间,使用app传输的数据量来统计
  55. // We are not tracking when the radio is up, so must approximate power use
  56. // based on the number of packets.
  57. app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets)
  58. * getMobilePowerPerPacket(rawRealtimeUs, statsType);
  59. }
  60. if (DEBUG && app.mobileRadioPowerMah != 0) {
  61. Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
  62. + (app.mobileRxPackets + app.mobileTxPackets)
  63. + " active time " + app.mobileActive
  64. + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah));
  65. }
  66. }
  1. <item name="radio.active">180.1</item>
  2. <item name="radio.scanning">90.1</item>
  3. <array name="radio.on">
  4. <value>6.2</value>
  5. <value>6.2</value>
  6. </array>

app使用耗电指是在radio.active的状态下,其他的参数在计算radio耗电的时候使用.
计算公式:
情况一:当追踪信号活动时间,即mobileActive > 0,采用:

mobileRadioPowerMah = (app.mobileActive mPowerRadioOn) / (1000 60* 60);

情况二:当没有追踪信号活动时间,则采用:

mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) * MobilePowerPerPacket

其中MobilePowerPerPacket = ((mPowerRadioOn / 3600) / mobilePps) / (60*60), mobilePps= (mobileRx + mobileTx)/radioDataUptimeMs

1.4 wifi 耗电

wifi耗电分两种: WifiPowerCalculator 和 WifiPowerEstimator.
WifiPowerCalculator: 通过wifi处于保持链接(wifi.controller.idle),接受数据(wifi.controller.rx),发送数据(wifi.controller.tx).三种状态来计算.
WifiPowerEstimator : 保持链接(wifi.on),发送或者接受数据(wifi.active),扫描周围的热点(wifi.scan)来估计(通过数据包的量).
使用哪个和机器有关:

  1. // checkHasWifiPowerReporting can change if we get energy data at a later point, so
  2. // always check this field.
  3. final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile);
  4. if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) {
  5. mWifiPowerCalculator = hasWifiPowerReporting ?
  6. new WifiPowerCalculator(mPowerProfile) :
  7. new WifiPowerEstimator(mPowerProfile);
  8. mHasWifiPowerReporting = hasWifiPowerReporting;
  9. }

其实还是取决于电源配置文件:

  1. <item name="wifi.controller.idle">0</item>
  2. <item name="wifi.controller.rx">0</item>
  3. <item name="wifi.controller.tx">0</item>

当配置为如上信息,就会使用WifiPowerEstimator类的方法进行估计.这里我们把两种方法都分析一下:

WifiPowerCalculator

从名字看出这个类是计算,也就是底层提供数据能够普计算
WifiPowerCalculator.java->calculateApp()

  1. public WifiPowerCalculator(PowerProfile profile) {
  2. // 获取三种状态的单位功耗
  3. mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
  4. mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
  5. mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
  6. }
  7. @Override
  8. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  9. long rawUptimeUs, int statsType) {
  10. final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
  11. if (counter == null) {
  12. return;
  13. }
  14. // 获取三种状态的占用时间
  15. final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
  16. final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
  17. final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
  18. app.wifiRunningTimeMs = idleTime + rxTime + txTime;
  19. mTotalAppRunningTime += app.wifiRunningTimeMs;
  20. // 依据三种不同的wifi状态去计算
  21. app.wifiPowerMah =
  22. ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))
  23. / (1000*60*60);
  24. mTotalAppPowerDrain += app.wifiPowerMah;
  25. // 统计数据包量 和 byte 数
  26. app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
  27. statsType);
  28. app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
  29. statsType);
  30. app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
  31. statsType);
  32. app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
  33. statsType);
  34. if (DEBUG && app.wifiPowerMah != 0) {
  35. Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" +
  36. txTime + "ms power=" + BatteryStatsHelper.makemAh(app.wifiPowerMah));
  37. }
  38. }
  39. // 统计在wifi耗电除了app耗电被report的还有哪些耗电,也就是这里的耗电是不包括app使用wifi耗电的.
  40. @Override
  41. public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
  42. long rawUptimeUs, int statsType) {
  43. Log.d(TAG,"step into calculateRemaining");
  44. final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity();
  45. final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
  46. final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
  47. final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
  48. app.wifiRunningTimeMs = Math.max(0,
  49. (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime);
  50. double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType)
  51. / (double)(1000*60*60);
  52. if (powerDrainMah == 0) {
  53. // Some controllers do not report power drain, so we can calculate it here.
  54. powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
  55. + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
  56. }
  57. Log.d(TAG,"powerDrainMah = " + powerDrainMah + ",mTotalAppPowerDrain =" + mTotalAppPowerDrain);
  58. app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
  59. if (DEBUG) {
  60. Log.d(TAG, "left over WiFi power: " + BatteryStatsHelper.makemAh(app.wifiPowerMah));
  61. }
  62. }
  63. @Override
  64. public void reset() {
  65. mTotalAppPowerDrain = 0;
  66. mTotalAppRunningTime = 0;
  67. }

不过,在4g项目中mTxCurrentMa,mRxCurrentMa,mIdleCurrentMa在电池配置文件中都是0. 耗电计算公式:
wifiPowerMah = ((idleTime mIdleCurrentMa) + (txTime mTxCurrentMa) + (rxTime mRxCurrentMa)) / (1000 60* 60);

WifiPowerEstimator

在4g项目中是通过该方式进行计算的,这里是进行了估算, WifiPowerEstimator->calculateApp

  1. public WifiPowerEstimator(PowerProfile profile) {
  2. mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
  3. mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON);// wifi.on
  4. mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN);// wifi.scan
  5. mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN);// wifi.batchedscan
  6. }
  7. /**
  8. * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
  9. */
  10. private static double getWifiPowerPerPacket(PowerProfile profile) {
  11. // wifi的bit速率
  12. final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
  13. final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
  14. / 3600;// PowerProfile.POWER_WIFI_ACTIVE->wifi.active
  15. return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048);// 计算每一个数据包的耗电量
  16. }
  17. @Override
  18. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  19. long rawUptimeUs, int statsType) {
  20. app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
  21. statsType);
  22. app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
  23. statsType);
  24. app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
  25. statsType);
  26. app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
  27. statsType);
  28. // 计算收发数据包的耗电量
  29. final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
  30. * mWifiPowerPerPacket;
  31. // 计算wifi保持连接的功耗
  32. app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
  33. mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
  34. final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
  35. // 计算wifi扫描时候的功耗
  36. final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
  37. final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
  38. // 计算wifi批量扫描的功耗
  39. double wifiBatchScanPower = 0;
  40. for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {// BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS = 5
  41. final long batchScanTimeMs =
  42. u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
  43. final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);
  44. wifiBatchScanPower += batchScanPower;
  45. }
  46. // 将四种耗电累加
  47. app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
  48. if (DEBUG && app.wifiPowerMah != 0) {
  49. Log.d(TAG, "UID " + u.getUid() + ": power=" +
  50. BatteryStatsHelper.makemAh(app.wifiPowerMah));
  51. }
  52. }

通过估计传输一个数据包的耗电来估计app使用wifi时的耗电.
计算公式:
wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;

其中:wifiPacketPower = (wifiRxPackets + wifiTxPackets) mWifiPowerPerPacket; wifiLockPower =
(wifiRunningTimeMs
mWifiPowerOn) / (1000 60 60); wifiScanPower = (wifiScanTimeMs mWifiPowerScan) / (1000 60* 60);

wifiBatchScanPower = ∑ (batchScanTimeMs mWifiPowerBatchScan) / (1000 60* 60) ,5次相加。

1.5 bluetooth耗电统计

  1. public BluetoothPowerCalculator(PowerProfile profile) {
  2. mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
  3. mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
  4. mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
  5. }

目前没有配置POWER_BLUETOOTH_CONTROLLER_IDLE,POWER_BLUETOOTH_CONTROLLER_RX,POWER_BLUETOOTH_CONTROLLER_TX.

  1. @Override
  2. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  3. long rawUptimeUs, int statsType) {
  4. final BatteryStats.ControllerActivityCounter counter = u.getBluetoothControllerActivity();
  5. if (counter == null) {
  6. return;
  7. }
  8. final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
  9. final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
  10. final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
  11. final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
  12. // 通过获取bluetooth的耗电,转换单位
  13. double powerMah = counter.getPowerCounter().getCountLocked(statsType)// 具体怎么获取的不知道.....
  14. / (double)(1000*60*60);
  15. if (powerMah == 0) {
  16. powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
  17. / (1000*60*60);
  18. }
  19. app.bluetoothPowerMah = powerMah;
  20. app.bluetoothRunningTimeMs = totalTimeMs;
  21. app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType);
  22. app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType);
  23. mAppTotalPowerMah += powerMah;
  24. mAppTotalTimeMs += totalTimeMs;
  25. }

计算公式:
powerMah = counter.getPowerCounter().getCountLocked(statsType)/ (double)(10006060);// 获bluetooth的总耗电,转换单位
如果获取的是0使用以下方式计算:
powerMah = ((idleTimeMs mIdleMa) + (rxTimeMs mRxMa) + (txTimeMs mTxMa)) / (100060*60);
不过在4g中,这么计算是0.

1.6 传感器耗电

  1. public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) {
  2. mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);//获取sensor的列表
  3. mGpsPowerOn = profile.getAveragePower(PowerProfile.POWER_GPS_ON);//GPS耗电统计放在Sensor耗电中统计
  4. }
  5. @Override
  6. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  7. long rawUptimeUs, int statsType) {
  8. // Process Sensor usage
  9. final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
  10. final int NSE = sensorStats.size();//获取总的sensor数量
  11. for (int ise = 0; ise < NSE; ise++) {// 计算每个sensor的耗电
  12. final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
  13. final int sensorHandle = sensorStats.keyAt(ise);
  14. final BatteryStats.Timer timer = sensor.getSensorTime();
  15. final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
  16. switch (sensorHandle) {
  17. case BatteryStats.Uid.Sensor.GPS:
  18. app.gpsTimeMs = sensorTime;
  19. app.gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000*60*60);
  20. break;
  21. default:// 除了GPS的sensor都在这计算
  22. final int sensorsCount = mSensors.size();
  23. for (int i = 0; i < sensorsCount; i++) {
  24. final Sensor s = mSensors.get(i);
  25. if (s.getHandle() == sensorHandle) {
  26. app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60);
  27. break;
  28. }
  29. }
  30. break;
  31. }
  32. }
  33. }

关于传感器的配置文件:

  1. <item name="gps.on">40.8</item>

GPS功耗计算公式:
gpsPowerMah = (app.gpsTimeMs mGpsPowerOn) / (1000 60 60);
sensorPowerMah = ∑ (sensorTime
s.getPower()) / (10006060);

1.7 Camera耗电统计

此块耗电并没有算入之前定义的DrainType.CAMERA.不知道google是还没完善还是处于什么考虑

  1. public CameraPowerCalculator(PowerProfile profile) {
  2. mCameraPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_CAMERA);
  3. }
  4. @Override
  5. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  6. long rawUptimeUs, int statsType) {
  7. // Calculate camera power usage. Right now, this is a (very) rough estimate based on the
  8. // average power usage for a typical camera application.
  9. // 用Camera打开的时间 * 单位时间功耗
  10. final BatteryStats.Timer timer = u.getCameraTurnedOnTimer();
  11. if (timer != null) {
  12. final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
  13. app.cameraTimeMs = totalTime;
  14. app.cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (1000*60*60);
  15. } else {
  16. app.cameraTimeMs = 0;
  17. app.cameraPowerMah = 0;
  18. }
  19. }

PowerProfile.POWER_CAMERA:

  1. <item name="camera.avg">401.2</item>

计算公式:
cameraPowerMah = (totalTime mCameraPowerOnAvg) / (1000 60 * 60)

1.8 Flashlight耗电统计

该耗电并没有算入DrainType.FLASHLIGHT.情况同Camera.统计闪光灯模块耗电,eg:拍照闪光灯,手电筒

  1. public FlashlightPowerCalculator(PowerProfile profile) {
  2. mFlashlightPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_FLASHLIGHT);
  3. }
  4. @Override
  5. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  6. long rawUptimeUs, int statsType) {
  7. // Calculate flashlight power usage. Right now, this is based on the average power draw
  8. // of the flash unit when kept on over a short period of time.
  9. // android 中亮度只有一个,iOS中有多个,所以这里就是: 时间 * 单位耗电
  10. final BatteryStats.Timer timer = u.getFlashlightTurnedOnTimer();
  11. if (timer != null) {
  12. final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
  13. app.flashlightTimeMs = totalTime;
  14. app.flashlightPowerMah = (totalTime * mFlashlightPowerOnAvg) / (1000*60*60);
  15. } else {
  16. app.flashlightTimeMs = 0;
  17. app.flashlightPowerMah = 0;
  18. }
  19. }

PowerProfile.POWER_FLASHLIGHT:

  1. <item name="camera.flashlight">239.6</item>

计算公式:
flashlightPowerMah = (totalTime mFlashlightPowerOnAvg) / (100060*60)

1.9 OS耗电

统计的过程中用osSipper保存系统耗电

  1. // osSipper被初始化过,也就是上面统计的uid里面有系统的uid,下面把系统的耗电累加起来
  2. if (osSipper != null) {// 长时间cpu唤醒,但是屏幕没有亮,该部分的耗算入OS耗电
  3. // The device has probably been awake for longer than the screen on
  4. // time and application wake lock time would account for. Assign
  5. // this remainder to the OS, if possible.
  6. // 该部分在上面分析过
  7. mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs,
  8. mRawUptimeUs, mStatsType);
  9. // OS耗电求和
  10. osSipper.sumPower();
  11. }

值得注意的是app的耗电,没有统计屏幕耗电.


2 硬件电量统计

BatteryStatsHelper.java—>processMiscUsage()

  1. private void processMiscUsage() {
  2. addUserUsage();
  3. addPhoneUsage();
  4. addScreenUsage();
  5. addWiFiUsage();
  6. addBluetoothUsage();
  7. addMemoryUsage();
  8. addIdleUsage(); // Not including cellular idle power
  9. // Don't compute radio usage if it's a wifi-only device
  10. if (!mWifiOnly) {
  11. addRadioUsage();
  12. }
  13. }

2.1 user耗电统计

属于BatterySipper.DrainType.USER

  1. private void addUserUsage() {
  2. for (int i = 0; i < mUserSippers.size(); i++) {
  3. final int userId = mUserSippers.keyAt(i);
  4. BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
  5. bs.userId = userId;
  6. // 将每个user用户的耗电求和
  7. aggregateSippers(bs, mUserSippers.valueAt(i), "User");
  8. mUsageList.add(bs);
  9. }
  10. }

aggregateSippers:

  1. private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
  2. for (int i = 0; i < from.size(); i++) {
  3. BatterySipper wbs = from.get(i);
  4. if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
  5. bs.add(wbs);
  6. }
  7. bs.computeMobilemspp();
  8. bs.sumPower();
  9. }

计算公式:
user_power = user_1_power + user_2_power + … + user_n_power; (n为所有的user的总数)

2.2 Phone耗电统计

属于:BatterySipper.DrainType.PHONE.计算通话耗电量

  1. private void addPhoneUsage() {
  2. long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, mStatsType) / 1000;
  3. // Phone的耗电也采用POWER_RADIO_ACTIVE,后面的radio的耗电统计也用到这个单位耗电量
  4. // 计算功耗
  5. double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
  6. * phoneOnTimeMs / (60*60*1000);
  7. // 保存
  8. if (phoneOnPower != 0) {
  9. addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
  10. }
  11. }

PowerProfile.POWER_RADIO_ACTIVE :

  1. <item name="radio.active">180.1</item>

计算公式:
phone_powers = (phoneOnTimeMs phoneOnPower) / (60 60* 1000)

2.3 屏幕耗电统计

属于:BatterySipper.DrainType.SCREEN.屏幕耗电是单独算的,没有算入app耗电

  1. /**
  2. * Screen power is the additional power the screen takes while the device is running.
  3. */
  4. private void addScreenUsage() {
  5. double power = 0;
  6. // 计算屏幕处于POWER_SCREEN_ON状态的功耗
  7. long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtimeUs, mStatsType) / 1000;
  8. power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
  9. // 获取屏幕最高亮度的单位耗电
  10. final double screenFullPower =
  11. mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
  12. // 计算不同亮度等级的耗电量,并进行统计.
  13. for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {// 5个亮度等级
  14. double screenBinPower = screenFullPower * (i + 0.5f)
  15. / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
  16. long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtimeUs, mStatsType)
  17. / 1000;
  18. double p = screenBinPower * brightnessTime;
  19. if (DEBUG && p != 0) {
  20. Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
  21. + " power=" + makemAh(p / (60 * 60 * 1000)));
  22. }
  23. power += p;
  24. }
  25. power /= (60 * 60 * 1000); // To hours
  26. if (power != 0) {
  27. addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
  28. }
  29. }

POWER_SCREEN_ON 和 POWER_SCREEN_FULL对应的值:

  1. <item name="screen.on">21.7</item>
  2. <item name="screen.full">304.3</item>// 不是最大亮度耗电,比最大亮度功耗大10%

屏幕亮度的等级:

  1. public static final int SCREEN_BRIGHTNESS_DARK = 0;
  2. public static final int SCREEN_BRIGHTNESS_DIM = 1;
  3. public static final int SCREEN_BRIGHTNESS_MEDIUM = 2;
  4. public static final int SCREEN_BRIGHTNESS_LIGHT = 3;
  5. public static final int SCREEN_BRIGHTNESS_BRIGHT = 4;
  6. static final String[] SCREEN_BRIGHTNESS_NAMES = {
  7. "dark", "dim", "medium", "light", "bright"
  8. };
  9. static final String[] SCREEN_BRIGHTNESS_SHORT_NAMES = {
  10. "0", "1", "2", "3", "4"
  11. };
  12. public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5;

计算公式:
公式:screen_power = screenOnTimeMs * screenOnPower + backlight_power

其中:backlight_power = 0.1 dark_brightness_time screenFullPower + 0.3 dim_brightness_time screenFullPower

  1. + 0.5 * medium_brightness_time * screenFullPower + 0.7 * light_brightness_time
  2. + 0.9 * bright_brightness_time * screenFullPower;

2.4 wifi耗电统计

属于 BatterySipper.DrainType.WIFI,每个app使用wifi的耗电计算了,这里统计的是所有app在wifi上耗电总和实际wifi运行耗电的差值.

  1. /**
  2. * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
  3. * then only the WiFi process gets blamed here since we normalize power calculations and
  4. * assign all the power drain to apps. If energy info is not reported, we attribute the
  5. * difference between total running time of WiFi for all apps and the actual running time
  6. * of WiFi to the WiFi subsystem.
  7. */
  8. private void addWiFiUsage() {
  9. BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);// DrainType.WIFI就是BatterySipper.DrainType.WIFI
  10. mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
  11. mStatsType);
  12. aggregateSippers(bs, mWifiSippers, "WIFI");
  13. if (bs.totalPowerMah > 0) {
  14. mUsageList.add(bs);
  15. }
  16. }

在我们的项目中使用的是WifiPowerEstimator.calculateRemaining():

  1. @Override
  2. public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
  3. long rawUptimeUs, int statsType) {
  4. // 获取毫秒级的wifi运行的总时间
  5. final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType)
  6. / 1000;
  7. // 这里的耗电是以保持连接计算的
  8. final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn)
  9. / (1000*60*60);
  10. app.wifiRunningTimeMs = totalRunningTimeMs;
  11. app.wifiPowerMah = Math.max(0, powerDrain);
  12. }

mWifiPowerOn:

  1. <item name="wifi.on">1.0</item>

这块的功耗还是比较低的.

2.5 蓝牙耗电统计

属于BatterySipper.DrainType.BLUETOOTH于此处统计的Bluetooth耗电是指app耗电之外的蓝牙耗电

  1. /**
  2. * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the
  3. * Bluetooth Category.
  4. */
  5. private void addBluetoothUsage() {
  6. BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
  7. mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
  8. mStatsType);
  9. // 对mBluetoothSippers里的统计量,进行求和,都算入Bluetooth
  10. aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
  11. if (bs.totalPowerMah > 0) {
  12. mUsageList.add(bs);// 保存到所有app的耗电统计结果清单中
  13. }
  14. }

看下具体计算:

  1. public BluetoothPowerCalculator(PowerProfile profile) {
  2. mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
  3. mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
  4. mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
  5. }

可是在电源配置文件中没有这三个值,使用默认值是0.

  1. @Override
  2. public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
  3. long rawUptimeUs, int statsType) {
  4. final BatteryStats.ControllerActivityCounter counter =
  5. stats.getBluetoothControllerActivity();
  6. final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
  7. final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
  8. final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
  9. final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
  10. double powerMah = counter.getPowerCounter().getCountLocked(statsType)
  11. / (double)(1000*60*60);
  12. if (powerMah == 0) {
  13. // Some devices do not report the power, so calculate it.
  14. powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
  15. / (1000*60*60);
  16. }
  17. // Subtract what the apps used, but clamp to 0.
  18. powerMah = Math.max(0, powerMah - mAppTotalPowerMah);
  19. if (DEBUG && powerMah != 0) {
  20. Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs)
  21. + " power=" + BatteryStatsHelper.makemAh(powerMah));
  22. }
  23. app.bluetoothPowerMah = powerMah;
  24. app.bluetoothRunningTimeMs = Math.max(0, totalTimeMs - mAppTotalTimeMs);
  25. }
  1. <item name="bluetooth.active">49.8</item>
  2. <item name="bluetooth.on">1.8</item>

也就是说bluetooth.active 和 bluetooth.on并没有使用.那么岂不是bluetooth耗电是0?
不是的,在统计app耗电的时候,当app的uid是Process.BLUETOOTH_UID就将其耗电计入到mBluetoothSippers.统计bluetooth耗电就将这些值求和. 后续该模块google可能还要修改.

2.6 内存耗电统计

属于BatterySipper.DrainType.MEMORY,这部分是android O才添加的

  1. private void addMemoryUsage() {
  2. BatterySipper memory = new BatterySipper(DrainType.MEMORY, null, 0);
  3. mMemoryPowerCalculator.calculateRemaining(memory, mStats, mRawRealtimeUs, mRawUptimeUs,
  4. mStatsType);
  5. // 汇总一下,和os耗电统计的sumPower()是同一个方法
  6. memory.sumPower();
  7. // 添加到app耗电的列表中
  8. if (memory.totalPowerMah > 0) {
  9. mUsageList.add(memory);
  10. }

MemoryPowerCalculator.calculateRemaining():

  1. public MemoryPowerCalculator(PowerProfile profile) {
  2. int numBuckets = profile.getNumElements(PowerProfile.POWER_MEMORY);
  3. powerAverages = new double[numBuckets];
  4. for (int i = 0; i < numBuckets; i++) {// 4g只有一个等级
  5. powerAverages[i] = profile.getAveragePower(PowerProfile.POWER_MEMORY, i);
  6. if (powerAverages[i] == 0 && DEBUG) {
  7. Log.d(TAG, "Problem with PowerProfile. Received 0 value in MemoryPowerCalculator");
  8. }
  9. }
  10. }
  11. // app使用Memory的耗电还是空方法,看来google在耗电统计方面还在做努力
  12. @Override
  13. public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
  14. long rawUptimeUs, int statsType) {}
  15. // 计算Memory的耗电
  16. @Override
  17. public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
  18. long rawUptimeUs, int statsType) {
  19. double totalMah = 0;
  20. long totalTimeMs = 0;
  21. // 获取kernel使用memery耗电的时间片段统计
  22. LongSparseArray<? extends BatteryStats.Timer> timers = stats.getKernelMemoryStats();
  23. for (int i = 0; i < timers.size() && i < powerAverages.length; i++) {
  24. // 获取单位时间片段的单位耗电
  25. double mAatRail = powerAverages[(int) timers.keyAt(i)];
  26. // 获取该时间片段的时长
  27. long timeMs = timers.valueAt(i).getTotalTimeLocked(rawRealtimeUs, statsType);
  28. double mAm = (mAatRail * timeMs) / (1000*60);
  29. if(DEBUG) {
  30. Log.d(TAG, "Calculating mAh for bucket " + timers.keyAt(i) + " while unplugged");
  31. Log.d(TAG, "Converted power profile number from "
  32. + powerAverages[(int) timers.keyAt(i)] + " into " + mAatRail);
  33. Log.d(TAG, "Calculated mAm " + mAm);
  34. }
  35. totalMah += mAm/60;
  36. totalTimeMs += timeMs;
  37. }
  38. app.usagePowerMah = totalMah;
  39. app.usageTimeMs = totalTimeMs;
  40. if (DEBUG) {
  41. Log.d(TAG, String.format("Calculated total mAh for memory %f while unplugged %d ",
  42. totalMah, totalTimeMs));
  43. }

PowerProfile.POWER_MEMORY:

  1. <array name="memory.bandwidths">
  2. <value>22.7</value>
  3. </array>

计算公式:
totalMah = ∑ (mAatRail timeMs) / (100060)

2.7 手机空闲耗电统计

统计的是基准功率的功耗,统计cpu在idle和awake的情况下,处于低电量状态时,只有idle状态的耗电,

  1. /**
  2. * Calculate the baseline power usage for the device when it is in suspend and idle.
  3. * The device is drawing POWER_CPU_IDLE power at its lowest power state.
  4. * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held.
  5. */
  6. private void addIdleUsage() {
  7. // 获取处于suspend状态的时间
  8. final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) *
  9. mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
  10. // 从android 7开始算入POWER_CPU_AWAKE状态的耗电
  11. final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) *
  12. mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
  13. final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000);
  14. if (DEBUG && totalPowerMah != 0) {
  15. Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000)
  16. + " power=" + makemAh(suspendPowerMaMs / (60 * 60 * 1000)));
  17. Log.d(TAG, "Idle: time=" + (mTypeBatteryUptimeUs / 1000)
  18. + " power=" + makemAh(idlePowerMaMs / (60 * 60 * 1000)));
  19. }
  20. if (totalPowerMah != 0) {
  21. addEntry(BatterySipper.DrainType.IDLE, mTypeBatteryRealtimeUs / 1000, totalPowerMah);
  22. }
  23. }

PowerProfile.POWER_CPU_IDLE 和 PowerProfile.POWER_CPU_AWAKE对应的值:

  1. <item name="cpu.idle">4.8</item>
  2. <item name="cpu.awake">21.1</item>

计算公式:
idlePower = (idleTimeMs cpuIdlePower + awakeTimeMs cpuAwakePower) / (60 60 1000)

值得注意的是: 该模块并不包括手机处于cell网络下的idle状态耗电.

2.8 Radio耗电统计

如果只有wifi的设备就不统计.addRadioUsage():

  1. private void addRadioUsage() {
  2. BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
  3. mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs,
  4. mStatsType);
  5. radio.sumPower();
  6. if (radio.totalPowerMah > 0) {
  7. mUsageList.add(radio);
  8. }
  9. }

看下MobileRadioPowerCalculator.calculateRemaining()方法是怎么计算的:

  1. @Override
  2. public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
  3. long rawUptimeUs, int statsType) {
  4. double power = 0;// 统计radio的最终耗电
  5. long signalTimeMs = 0;// 和cell连接的总时长
  6. long noCoverageTimeMs = 0;// 没有信号的时间长度
  7. for (int i = 0; i < mPowerBins.length; i++) {// 实测该模块这部分耗电最大,由于时间大.
  8. long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType)
  9. / 1000;
  10. final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000);
  11. if (DEBUG && p != 0) {
  12. Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
  13. + BatteryStatsHelper.makemAh(p));
  14. }
  15. power += p;
  16. signalTimeMs += strengthTimeMs;
  17. if (i == 0) {// i = 0是代表没有信号的时间长度(none)
  18. noCoverageTimeMs = strengthTimeMs;
  19. }
  20. }
  21. // 计算扫描时的耗电
  22. final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)
  23. / 1000;
  24. final double p = (scanningTimeMs * mPowerScan) / (60*60*1000);
  25. if (DEBUG && p != 0) {
  26. Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs
  27. + " power=" + BatteryStatsHelper.makemAh(p));
  28. }
  29. power += p;
  30. // 计算Active状态的耗电,排除app使用cell的耗电
  31. long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
  32. // 排除统计了的app使用耗电,app的cell耗电,只统计处于active状态下的耗电
  33. long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;
  34. if (remainingActiveTimeMs > 0) {
  35. power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60);
  36. }
  37. // 保存
  38. if (power != 0) {
  39. if (signalTimeMs != 0) {// 无信号百分比
  40. app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
  41. }
  42. app.mobileActive = remainingActiveTimeMs;// radio活动,除app
  43. app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);// 获取radio的活跃次数
  44. app.mobileRadioPowerMah = power;// 获取连接cell的功耗
  45. }
  46. }

计算公式:
mobileRadioPowerMah = strengthOnPower + scanningPower + remainingActivePower

其中: strengthOnPower = none_strength_Ms none_strength_Power + poor_strength_Ms poor_strength_Power

  1. + moderate_strength_Ms * moderate_strength_Power + good_strength_Ms * good_strength_Power
  2. + great_strength_Ms * great_strength_Power

scanningPower = scanningTimeMs * mPowerScan;

remainingActivePower = (radioActiveTimeMs - mTotalAppMobileActiveMs)* mPowerRadioOn;
该模块统计是排除了app的使用网络造成的耗电.