一、 概述

Android系统中的耗电统计分为软件排行榜和硬件排行榜,软件排序榜是统计每个App的耗电总量的排行榜,硬件排行榜则是统计主要硬件的耗电总量的排行榜。

涉及耗电统计相关的核心类:

/framework/base/core/res/res/xml/power_profile.xml
/framework/base/core/java/com/andorid/internal/os/PowerProfile.java
/framework/base/core/java/com/andorid/internal/os/BatteryStatsHelper.java
/framework/base/core/java/com/andorid/internal/os/BatterySipper.java
  • PowerProfile.java用于获取各个组件的电流数值;power_profile.xml是一个可配置的功耗数据文件。
  • 软件排行榜的计算算法:BatteryStatsHelper类中的processAppUsage()方法
  • 硬件排行榜的计算算法:BatteryStatsHelper类中的processMiscUsage()方法

下面直接切入主题,分别讲述软件和硬件的耗电统计

二、软件排行榜

processAppUsage统计每个App的耗电情况

private void processAppUsage(SparseArray<UserHandle> asUsers) {//判断是否统计所有用户的App耗电使用情况,目前该参数为truefinal boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);mStatsPeriod = mTypeBatteryRealtime; //耗电的统计时长BatterySipper osSipper = null;//获取每个uid的统计信息final SparseArray<? extends Uid> uidStats = mStats.getUidStats();final int NU = uidStats.size();// 开始遍历每个uid的耗电情况for (int iu = 0; iu < NU; iu++) {final Uid u = uidStats.valueAt(iu);final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);final double totalPower = app.sumPower(); //对App的8项耗电再进行累加。//将app添加到 app list, WiFi, Bluetooth等,或其他用户列表if (totalPower != 0 || u.getUid() == 0) {final int uid = app.getUid();final int userId = UserHandle.getUserId(uid);if (uid == Process.WIFI_UID) { //uid为wifi的情况mWifiSippers.add(app);} else if (uid == Process.BLUETOOTH_UID) {//uid为蓝牙的情况mBluetoothSippers.add(app);} else if (!forAllUsers && asUsers.get(userId) == null&& UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {//就目前来说 forAllUsers=true,不会进入此分支。List<BatterySipper> list = mUserSippers.get(userId);if (list == null) {list = new ArrayList<>();mUserSippers.put(userId, list);}list.add(app);} else {mUsageList.add(app);  //把app耗电加入到mUsageList}if (uid == 0) {osSipper = app; // root用户,代表操作系统的耗电量}}}//app之外的耗电量if (osSipper != null) {mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime, mRawUptime, mStatsType);osSipper.sumPower();}
}

流程分析:mTypeBatteryRealtime

mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;

BatteryStats.STATS_SINCE_CHARGED,计算规则是从上次充满电后数据;另外STATS_SINCE_UNPLUGGED是拔掉USB线后的数据。说明充电时间的计算是从上一次拔掉设备到现在的耗电量统计。

耗电计算项

8大模块的耗电计算器,都继承与PowerCalculator抽象类

计算项 Class文件
CPU功耗 mCpuPowerCalculator.java
Wakelock功耗 mWakelockPowerCalculator.java
无线电功耗 mMobileRadioPowerCalculator.java
WIFI功耗 mWifiPowerCalculator.java
蓝牙功耗 mBluetoothPowerCalculator.java
Sensor功耗 mSensorPowerCalculator.java
相机功耗 mCameraPowerCalculator.java
闪光灯功耗 mFlashlightPowerCalculator.java

计算值添加到列表

  • mWifiSippers.add(app): uid为wifi的情况
  • mBluetoothSippers.add(app): uid为蓝牙的情况
  • mUsageList.add(app): app耗电加入到mUsageList
  • osSipper: root用户,代表操作系统的耗电量,app之外的wakelock耗电也计算该项

公式

processAppUsage统计的是Uid。一般地来说每个App都对应一个Uid,但存在以下特殊情况,如果两个或多个App签名和sharedUserId相同,则在运行时,他们拥有相同Uid。对于系统应用uid为system,则统一算入system应用的耗电量。

Uid_Power = process_1_Power + … + process_N_Power,其中所有进程都是属于同一个uid。 当同一的uid下,只有一个进程时,Uid_Power = process_Power;

其中process_Power = CPU功耗 + Wakelock功耗 + 无线电功耗 + WIFI功耗 + 蓝牙功耗 + Sensor功耗 + 相机功耗 + 闪光灯功耗。 接下来开始分配说明每一项的功耗计算公式。

2.1 CPU

CPU功耗项的计算是通过CpuPowerCalculator类,初始化:

public CpuPowerCalculator(PowerProfile profile) {final int speedSteps = profile.getNumSpeedSteps(); //获取cpu的主频等级的级数mPowerCpuNormal = new double[speedSteps]; //用于记录不同频率下的功耗值mSpeedStepTimes = new long[speedSteps];for (int p = 0; p < speedSteps; p++) {mPowerCpuNormal[p] = profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);}
}

从对象的构造方法,可以看出PowerProfile类提供相应所需的基础功耗值,而真正的功耗值数据来源于power_profile.xml文件,故可以通过配置合理的基础功耗值,来达到较为精准的功耗统计结果。后续所有的PowerCalculator子类在构造方法中都有会相应所需的功耗配置项。

CPU功耗可配置项:

  • POWER_CPU_SPEEDS = “cpu.speeds” 所对应的数组,配置CPU的频点,频点个数取决于CPU硬件特性
  • POWER_CPU_ACTIVE = “cpu.active” 所对应的数组,配置CPU频点所对应的单位功耗(mAh)

功耗计算

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {final int speedSteps = mSpeedStepTimes.length;long totalTimeAtSpeeds = 0;for (int step = 0; step < speedSteps; step++) {//获取Cpu不同频点下的运行时间mSpeedStepTimes[step] = u.getTimeAtCpuSpeed(step, statsType);totalTimeAtSpeeds += mSpeedStepTimes[step];}totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1); //获取Cpu总共的运行时间app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; //获取cpu在用户态和内核态的执行时长double cpuPowerMaMs = 0;// 计算Cpu的耗电量for (int step = 0; step < speedSteps; step++) {final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds;final double cpuSpeedStepPower = ratio * app.cpuTimeMs * mPowerCpuNormal[step];cpuPowerMaMs += cpuSpeedStepPower;}//追踪不同进程的耗电情况double highestDrain = 0;app.cpuFgTimeMs = 0;final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();final int processStatsCount = processStats.size();//统计同一个uid的不同进程的耗电情况for (int i = 0; i < processStatsCount; i++) {final BatteryStats.Uid.Proc ps = processStats.valueAt(i);final String processName = processStats.keyAt(i);app.cpuFgTimeMs += ps.getForegroundTime(statsType);final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType) + ps.getForegroundTime(statsType);//App可以有多个packages和多个不同的进程,跟踪耗电最大的进程if (app.packageWithHighestDrain == null ||app.packageWithHighestDrain.startsWith("*")) {highestDrain = costValue;app.packageWithHighestDrain = processName;} else if (highestDrain < costValue && !processName.startsWith("*")) {highestDrain = costValue;app.packageWithHighestDrain = processName;}}//当Cpu前台时间 大于Cpu时间,将cpuFgTimeMs赋值为cpuTimeMsif (app.cpuFgTimeMs > app.cpuTimeMs) {app.cpuTimeMs = app.cpuFgTimeMs;}app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000); //转换为mAh}
}

子公式

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频点个数)

2.2 Wakelock

Wakelock功耗项的计算是通过WakelockPowerCalculator类

初始化

public WakelockPowerCalculator(PowerProfile profile) {mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
}

Wakelock功耗可配置项:power_profile.xml文件:

  • POWER_CPU_AWAKE = “cpu.awake” 所对应的值

功耗计算

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {long wakeLockTimeUs = 0;final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =u.getWakelockStats();final int wakelockStatsCount = wakelockStats.size();for (int i = 0; i < wakelockStatsCount; i++) {final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i);//只统计partial wake locks,由于 full wake locks在灭屏后会自动取消BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);if (timer != null) {wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);}}app.wakeLockTimeMs = wakeLockTimeUs / 1000; //转换单位为秒mTotalAppWakelockTimeMs += app.wakeLockTimeMs;//计算唤醒功耗app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60);
}

子公式

wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (10006060);

2.3 Wifi

WifiPowerCalculator

Wifi功耗项的计算是通过WifiPowerCalculator类

初始化

public WifiPowerCalculator(PowerProfile profile) {mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);}

Wifi功耗可配置项:

  • POWER_WIFI_CONTROLLER_IDLE = “wifi.controller.idle” 项所对应的值
  • POWER_WIFI_CONTROLLER_RX = “wifi.controller.rx” 项所对应的值
  • POWER_WIFI_CONTROLLER_TX = “wifi.controller.tx” 项所对应的值

功耗计算

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME, statsType);final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);app.wifiRunningTimeMs = idleTime + rxTime + txTime; //计算wifi的时间app.wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) / (1000*60*60);mTotalAppPowerDrain += app.wifiPowerMah;app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,statsType);app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,statsType);app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,statsType);app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,statsType);
}

子公式

wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) / (1000* 60* 60);

WifiPowerEstimator

可配置项

  • POWER_WIFI_ACTIVE = “wifi.active”(用于计算mWifiPowerPerPacket)
  • POWER_WIFI_ON=”wifi.on”
  • POWER_WIFI_SCAN = “wifi.scan”
  • POWER_WIFI_BATCHED_SCAN = “wifi.batchedscan”;

功耗计算-

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,statsType);app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,statsType);app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,statsType);app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,statsType);final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)* mWifiPowerPerPacket;app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);double wifiBatchScanPower = 0;for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {final long batchScanTimeMs =u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);wifiBatchScanPower += batchScanPower;}app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
}

其中 BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS=5

另外

private static double getWifiPowerPerPacket(PowerProfile profile) {final long WIFI_BPS = 1000000; //初略估算每秒的收发1000000bitfinal double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)/ 3600;return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
}

子公式

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次相加。

2.4 Bluetooth

Bluetooth功耗项的计算是通过BluetoothPowerCalculator类

初始化

public BluetoothPowerCalculator(PowerProfile profile) {mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
}

Bluetooth功耗可配置项(目前蓝牙功耗计算的方法为空,此配置暂可忽略):

  • POWER_BLUETOOTH_CONTROLLER_IDLE = “bluetooth.controller.idle” 所对应的值
  • POWER_BLUETOOTH_CONTROLLER_RX = “bluetooth.controller.rx” 所对应的值
  • POWER_BLUETOOTH_CONTROLLER_TX = “bluetooth.controller.tx” 所对应的值

功耗计算

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {// No per-app distribution yet.
}

子公式

bluePower = 0;还没有给每个App统计蓝牙的算法。

2.5 Camera

Camera功耗项的计算是通过CameraPowerCalculator类

初始化

public CameraPowerCalculator(PowerProfile profile) {mCameraPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_CAMERA);
}

Camera功耗可配置项:

  • POWER_CAMERA = “camera.avg” 所对应的值

功耗计算

 public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {//对于camera功耗的评估比较粗,camera打开认定功耗一致final BatteryStats.Timer timer = u.getCameraTurnedOnTimer();if (timer != null) {final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;app.cameraTimeMs = totalTime;app.cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (1000*60*60);} else {app.cameraTimeMs = 0;app.cameraPowerMah = 0;}
}

子公式

cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (10006060);

2.6 Flashlight

Flashlight功耗项的计算是通过FlashlightPowerCalculator类

初始化

public FlashlightPowerCalculator(PowerProfile profile) {mFlashlightPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_FLASHLIGHT);
}

Flashlight功耗可配置项:

  • POWER_FLASHLIGHT = “camera.flashlight” 所对应的值

功耗计算

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {final BatteryStats.Timer timer = u.getFlashlightTurnedOnTimer();if (timer != null) {final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;app.flashlightTimeMs = totalTime;app.flashlightPowerMah = (totalTime * mFlashlightPowerOnAvg) / (1000*60*60);} else {app.flashlightTimeMs = 0;app.flashlightPowerMah = 0;}
}

子公式

flashlightPowerMah = (totalTime * mFlashlightPowerOnAvg) / (10006060);

flashlight计算方式与Camera功耗计算思路一样。

2.7 MobileRadio

无线电功耗项的计算是通过MobileRadioPowerCalculator类

初始化

public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) {mPowerRadioOn = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE);for (int i = 0; i < mPowerBins.length; i++) {mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i);}mPowerScan = profile.getAveragePower(PowerProfile.POWER_RADIO_SCANNING);mStats = stats;
}

无线电功耗可配置项:

  • POWER_RADIO_ACTIVE = “radio.active” 所对应的值

功耗计算

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, statsType);app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, statsType);app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000;app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,statsType);app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,statsType);if (app.mobileActive > 0) {// 当追踪信号的开头,则采用mobileActive时间乘以相应功耗mTotalAppMobileActiveMs += app.mobileActive;app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60);} else {//当没有追踪信号开关,则采用收发数据包来评估功耗app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) * getMobilePowerPerPacket(rawRealtimeUs, statsType);}
}

计算出每收/发一个数据包的功耗值

private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {final long MOBILE_BPS = 200000; //Extract average bit rates from systemfinal double MOBILE_POWER = mPowerRadioOn / 3600;final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,statsType);final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,statsType);final long mobileData = mobileRx + mobileTx;final long radioDataUptimeMs =mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)? (mobileData / (double)radioDataUptimeMs): (((double)MOBILE_BPS) / 8 / 2048);return (MOBILE_POWER / mobilePps) / (60*60);
}

子公式

情况一:当追踪信号活动时间,即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

2.8 Sensor

Sensor功耗项的计算是通过SensorPowerCalculator类

初始化

public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) {mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);mGpsPowerOn = profile.getAveragePower(PowerProfile.POWER_GPS_ON);
}

Sensor功耗可配置项:

  • POWER_GPS_ON = “gps.on” 所对应的值

功耗计算

d calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {// 计算没有个Uidfinal SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();final int NSE = sensorStats.size();for (int ise = 0; ise < NSE; ise++) {final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);final int sensorHandle = sensorStats.keyAt(ise);final BatteryStats.Timer timer = sensor.getSensorTime();final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;switch (sensorHandle) {case BatteryStats.Uid.Sensor.GPS:  //对于uid为GPS的情况app.gpsTimeMs = sensorTime;app.gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000*60*60);break;default:final int sensorsCount = mSensors.size();for (int i = 0; i < sensorsCount; i++) { //统计所有的sensor情况final Sensor s = mSensors.get(i);if (s.getHandle() == sensorHandle) {app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60);break;}}break;}}
}

GPS的功耗计算与sensor的计算方法放在一块

子公式

gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000* 60* 60);

软件功耗总公式

BatterySipper中,功耗计算方法:

public double sumPower() {return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +flashlightPowerMah;
}

软件功耗的子项共分为9项:

功耗项 解释
usage 通用的功耗
cpu cpu的消耗
wakelock 唤醒带来的功耗
mobileRadio 移动无线的功耗
wifi wifi功耗
gps 定位的功耗
sensor 传感器的功耗
camera 相机功耗
flashlight 闪光灯功耗

目前没有统计蓝牙的耗电,源码中统计蓝牙耗电的方法是空方法。gps耗电的计算在sensor模块一起计算的。

软件功耗总公式

三、 硬件排行榜

processMiscUsage()

private void processMiscUsage() {addUserUsage();addPhoneUsage();addScreenUsage();addWiFiUsage();addBluetoothUsage();addIdleUsage();if (!mWifiOnly) { //对于只有wifi上网功能的设备,将不算计此项addRadioUsage();}
}

硬件功耗的子项共分为7项:

功耗项 解释
UserUsage 用户功耗
PhoneUsage 通话功耗
ScreenUsage 屏幕功耗
WiFiUsage Wifi功耗
BluetoothUsage 蓝牙消耗
IdleUsage CPU Idle功耗
RadioUsage 移动无线功耗

3.1 User

用户功耗的类型DrainType.USER

功耗计算

private void addUserUsage() {for (int i = 0; i < mUserSippers.size(); i++) {final int userId = mUserSippers.keyAt(i);BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);bs.userId = userId;aggregateSippers(bs, mUserSippers.valueAt(i), "User"); //将UserSippers中的功耗都合入bs.mUsageList.add(bs);}
}

合计

private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {for (int i=0; i<from.size(); i++) {BatterySipper wbs = from.get(i);bs.add(wbs);  //将from中的功耗数据合入bs}bs.computeMobilemspp();bs.sumPower(); //计算总功耗
}

子公式

user_power = user_1_power + user_2_power + … + user_n_power; (n为所有的user的总数)

3.2 Phone

通话功耗的类型为DrainType.PHONE

通话功耗可配置项:

  • POWER_RADIO_ACTIVE = “radio.active” 所对应的值

功耗计算

private void addPhoneUsage() {long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) * phoneOnTimeMs / (60*60*1000);if (phoneOnPower != 0) {addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);}
}

子公式

phone_powers = (phoneOnTimeMs * phoneOnPower) / (60* 60* 1000)

3.3 CPU Idle

CPU Idle功耗的类型为DrainType.IDLE

CPU Idle功耗可配置项:

  • PowerProfile.POWER_CPU_IDLE = “cpu.idle” 所对应的值

功耗计算

private void addIdleUsage() {long idleTimeMs = (mTypeBatteryRealtime - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) / (60*60*1000);if (idlePower != 0) {addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);}
}

子公式

idlePower = (idleTimeMs * cpuIdlePower) / (60* 60* 1000)

3.4 Screen

屏幕功耗的类型为DrainType.SCREEN

屏幕功耗可配置项

  • POWER_SCREEN_ON = “screen.on” 所对应的值
  • POWER_SCREEN_FULL = “screen.full” 所对应的值

功耗计算

private void addScreenUsage() {double power = 0;long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);final double screenFullPower =mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {double screenBinPower = screenFullPower * (i + 0.5f)/ BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType) / 1000;double p = screenBinPower*brightnessTime;power += p;}power /= (60*60*1000); // To hoursif (power != 0) {addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);}
}

其中参数:

BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS = 5;static final String[] SCREEN_BRIGHTNESS_NAMES = {"dark", "dim", "medium", "light", "bright"};static final String[] SCREEN_BRIGHTNESS_SHORT_NAMES = {"0", "1", "2", "3", "4"
};

子公式

公式:screen_power = screenOnTimeMs * screenOnPower + backlight_power

其中:backlight_power = 0.1 * dark_brightness_time * screenFullPower + 0.3 * dim_brightness_time * screenFullPower + 0.5 * medium_brightness_time * screenFullPower + 0.7 * light_brightness_time + 0.9 * bright_brightness_time * screenFullPower;

3.5 Wifi

WifiPowerCalculater

Wifi的硬件功耗的类型DrainType.WIFI。

Wifi功耗可配置项

power_profile.xml文件:

  • POWER_WIFI_CONTROLLER_IDLE = “wifi.controller.idle” 所对应的值
  • POWER_WIFI_CONTROLLER_RX = “wifi.controller.rx” 所对应的值
  • POWER_WIFI_CONTROLLER_TX = “wifi.controller.tx” 所对应的值

功耗计算

wifi的硬件功耗是指除去App消耗之外的剩余wifi功耗

private void addWiFiUsage() {BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType); //计算wifi硬件功耗aggregateSippers(bs, mWifiSippers, "WIFI"); //将Wifi硬件合计入总功耗if (bs.totalPowerMah > 0) {mUsageList.add(bs);}
}

计算

@Override
public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME, statsType);final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN, statsType) / (double)(1000*60*60);if (powerDrainMah == 0) {//对于不直接报告功耗的组件,可通过直接计算的方式来获取powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);}app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
}

子公式 公式:wifiPowerMah = powerDrainMah - mTotalAppPowerDrain;

powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) +(rxTimeMs * mRxCurrentMa);

mTotalAppPowerDrain是通过calculateApp计算过程中获取,记录所有App的wifi功耗值之和。

WifiPowerEstimator

可配置项

POWER_WIFI_ON=”wifi.on”

功耗计算

public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn) / (1000*60*60);app.wifiRunningTimeMs = totalRunningTimeMs;app.wifiPowerMah = Math.max(0, powerDrain);
}

子公式

wifiPowerMah = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn) / (1000* 60* 60);

3.6 Bluetooth

蓝牙功耗的类型DrainType.BLUETOOTH

蓝牙功耗可配置项

  • POWER_BLUETOOTH_CONTROLLER_IDLE = “bluetooth.controller.idle” 所对应的值
  • POWER_BLUETOOTH_CONTROLLER_RX = “bluetooth.controller.rx” 所对应的值
  • POWER_BLUETOOTH_CONTROLLER_TX = “bluetooth.controller.tx” 所对应的值

功耗计算

private void addBluetoothUsage() {BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); //将蓝牙功耗合计入总功耗if (bs.totalPowerMah > 0) {mUsageList.add(bs);}
}

计算:

public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {final long idleTimeMs = stats.getBluetoothControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME, statsType);final long txTimeMs = stats.getBluetoothControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);final long rxTimeMs = stats.getBluetoothControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;double powerMah = stats.getBluetoothControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN, statsType) / (double)(1000*60*60);if (powerMah == 0) {//对于不直接报告功耗的组件,可通过直接计算的方式来获取powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) / (1000*60*60);}app.usagePowerMah = powerMah;app.usageTimeMs = totalTimeMs;
}

子公式

公式:usagePowerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) / (1000 * 60 * 60);

蓝牙的计算与3.5 wifi计算方式相仿。

3.7 MobileRadio

无线电功耗的类型为DrainType.CELL

无线电功耗可配置项

power_profile.xml文件:

  • POWER_RADIO_ON = “radio.on” 所对应的数组,该数组大小为NUM_SIGNAL_STRENGTH_BINS = 5
  • POWER_RADIO_SCANNING = “radio.scanning” 所对应的值

功耗计算

对于只支持wifi的设备则不需要计算该项

private void addRadioUsage() {BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime, mStatsType);radio.sumPower();if (radio.totalPowerMah > 0) {mUsageList.add(radio);}
}

计算

@Override
public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {double power = 0;long signalTimeMs = 0;long noCoverageTimeMs = 0;for (int i = 0; i < mPowerBins.length; i++) {long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType)/ 1000;final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000);power += p;signalTimeMs += strengthTimeMs;if (i == 0) {noCoverageTimeMs = strengthTimeMs;}}final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)/ 1000;final double p = (scanningTimeMs * mPowerScan) / (60*60*1000);power += p;long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;if (remainingActiveTimeMs > 0) {power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60);}if (power != 0) {if (signalTimeMs != 0) {app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;}app.mobileActive = remainingActiveTimeMs;app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);app.mobileRadioPowerMah = power;}
}

常量:

public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
public static final String[] SIGNAL_STRENGTH_NAMES = {"none", "poor", "moderate", "good", "great"
};

子公式

mobileRadioPowerMah = strengthOnPower + scanningPower + remainingActivePower

其中: strengthOnPower = none_strength_Ms * none_strength_Power + poor_strength_Ms * poor_strength_Power + moderate_strength_Ms * moderate_strength_Power + good_strength_Ms * good_strength_Power + great_strength_Ms * great_strength_Power;

scanningPower = scanningTimeMs * mPowerScan;

remainingActivePower = (radioActiveTimeMs - mTotalAppMobileActiveMs)* mPowerRadioOn;

硬件耗电总公式

四、总结

(1) 关于WifiPowerCalculator && WifiPowerEstimator的选择问题

在 BatteryStatsHelper.java中:

final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile);if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) {mWifiPowerCalculator = hasWifiPowerReporting ?new WifiPowerCalculator(mPowerProfile) :new WifiPowerEstimator(mPowerProfile);mHasWifiPowerReporting = hasWifiPowerReporting;}
  • 当hasWifiPowerReporting = true时,采用WifiPowerCalculator算法;
  • 当hasWifiPowerReporting = false时,采用WifiPowerEstimator算法;

其中

public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {return stats.hasWifiActivityReporting() &&profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
}

mHasWifiActivityReporting的默认值为false,故WIFI计算方式默认采用WifiPowerEstimator方式。

(2) 硬件与软件排行榜,功耗项对比:

功耗项 软件榜 硬件榜
CPU
无线电
WIFI
蓝牙
Wakelock -
Sensor -
相机 -
闪光灯 -
通话 -
屏幕 -
用户 -

其中√代表包含此功耗项。

(3) 硬件与软件排行榜,可配置项对比:

对于wifi的统计存在两种方式.

功耗项 软件榜 硬件榜
CPU cpu.active cpu.idle
无线电 radio.active radio.on/scanning
WIFI_Calculater wifi.controller.idle/rx/tx wifi.controller.idle/rx/tx
WIFI_Estimator wifi.on/ scan/ batchedscan wifi.on
蓝牙 bluetooth.controller.idle/rx/tx bluetooth.controller.idle/rx/tx
Wakelock cpu.awake -
Sensor gps.on -
相机 camera.avg -
闪光灯 camera.flashlight -
通话 - radio.active
屏幕 - screen.on
用户 -  

只有通过配置文件,精确地配置好每一项的基础功耗值,才能有一个精确的功耗统计结果

Android耗电统计算法相关推荐

  1. Android电量统计

    Android电量统计 前言 在维护电量管家应用以及学习处理一些功耗问题的时候,经常会接触电量统计相关的知识,抽空总结下这块知识,方便自己以及他人的学习. 电量统计 概述 在Andorid系统中的电量 ...

  2. android 耗电分析工具,使用Battery Historian工具分析Android耗电分析

    Android8.0以上分析使用Energy Profiler,低版本使用Battery Historian,为了降低配置的难度建议使用docker容器运行Battery Historian镜像 在d ...

  3. Android耗电原理及飞书耗电治理

    这篇文章中简绍Android的耗电原理,以及飞书的耗电治理规划. Android耗电统计原理 我们先了解一下Android系统是如何进行耗电的统计的,最精确的方式当然是使用电流仪来进行统计,但是正常状 ...

  4. Android流量统计TrafficStats类

    对于Android流量统计来说在2.2版中新加入了TrafficStats类可以轻松获取,其实本身TrafficStats类也是读取Linux提供的文件对象系统类型的文本进行解析. android.n ...

  5. 你知道吗?其实 Oracle 直方图自动统计算法存在这些缺陷!(附验证步骤)

    作者 | 吴海存 责编 | Carol 出品 | CSDN 云计算(ID:CSDNcloud) 封图| CSDN下载于视觉中国 在某些场景下,表中某一列的数据分布会比较崎岖,使得CBO(cost ba ...

  6. android 双卡流量统计,android流量统计

    android流量统计 (2012-07-31 12:28:34) 标签: 杂谈 研究过一段时间的android流量统计 发个自己的总结帖 1 android有一个TrafficStats类可以直接获 ...

  7. oracle 自动表分析,其实 Oracle 直方图自动统计算法存在这些缺陷!

    原标题:其实 Oracle 直方图自动统计算法存在这些缺陷! 科技细分领域TOP10影响力内容第一季度入选作品 来源 | CSDN 作者 | 吴海存 在某些场景下,表中某一列的数据分布会比较崎岖,使得 ...

  8. Android耗电优化

    Android耗电优化 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq980106800/article/details/87811924 什么 ...

  9. 人头识别与计数_基于人头检测的人数统计算法研究

    摘要: 在统计人数时候都是根据所检测到的目标进行计数,从最开始传统的红外线检测的人数统计系统到现在的智能视频处理人数统计系统.在一些人群众多的场所和人员并排走时会有很严重的遮挡现象,所以红外线检测就不 ...

最新文章

  1. POJ 2195 Going Home 二分图的最大权匹配
  2. webrtc java api_WEBRTC--简单入门实例
  3. 嵌入式linux文件系统启动,嵌入式Linux之文件系统启动分析【原创】
  4. python图像几何变换_Python 图像处理 OpenCV (5):图像的几何变换
  5. 【Java】计算二进制数中1的个数
  6. Python入门1_数字表达式
  7. 【收藏】图片垂直居中的解决办法
  8. B/S VS C/S
  9. matlab线性代数电子书,实用大众线性代数 MATLAB版_13652907.pdf
  10. UE4 讯飞语音识别插件
  11. 沉痛哀悼我们的电骡和BT中国联盟
  12. 山经·南山经:猨翼山 [yuán yì shān]
  13. uclinux系统简介
  14. 科普爱好者不可错过的11本科技简史
  15. 2014年AVAYA IP Office 合作伙伴大会在北京举行,协作成长,乐在德云
  16. ant Design vue中a-row 内容不会垂直居中
  17. JavaWeb和WebGIS学习笔记(三)——GeoServer 发布shp数据地图
  18. 对象数组排序,利用jquery
  19. html实现学生系统,基于HTML5的学生信息管理系统的设计与实现
  20. Delphi下POS打印机,控制开钱箱,客显,顾客显示屏,小票打印机

热门文章

  1. 提前祝福你和你和家人国庆节快乐,旅途愉快!
  2. Java-Tcp/Ip-CS控制台聊天应用Demo
  3. Android studio: The number of method references in a .dex file cannot exceed 64K. Learn how to resol
  4. 双路服务器芯片组的发展
  5. Java—二维码生成与识别(一)
  6. crontab一些常用的语法 每天凌晨1点重启一遍
  7. python实现千牛客服自动回复语_客服自动回复设置技巧,别再傻傻全部自己回复了...
  8. 【工业】工业无线组网场景下的典型拓扑及设备解决方案
  9. QStringLiteral(str)
  10. 作为Fab-Liter战略的一部份,安森美剥离晶圆制造厂