Android功耗统计算法
Posted hellokitty2
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android功耗统计算法相关的知识,希望对你有一定的参考价值。
基于android 6.0的源码剖析。
Power_profile.xml文件demo:
<?xml version="1.0" encoding="utf-8"?> <!-- ** ** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License") ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ --> <device name="Android"> <!-- Most values are the incremental current used by a feature, in mA (measured at nominal voltage). The default values are deliberately incorrect dummy values. OEM\'s must measure and provide actual values before shipping a device. Example real-world values are given in comments, but they are totally dependent on the platform and can vary significantly, so should be measured on the shipping platform with a power meter. --> <item name="none">0</item> <item name="screen.on">200</item> <!-- ~200mA --> <item name="screen.full">300</item> <!-- ~300mA --> <item name="bluetooth.active">80</item> <!-- Bluetooth data transfer, ~10mA --> <item name="bluetooth.on">1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA --> <item name="wifi.on">3</item> <!-- ~3mA --> <item name="wifi.active">200</item> <!-- WIFI data transfer, ~200mA --> <item name="wifi.scan">100</item> <!-- WIFI network scanning, ~100mA --> <item name="dsp.audio">10</item> <!-- ~10mA --> <item name="dsp.video">50</item> <!-- ~50mA --> <item name="camera.flashlight">160</item> <!-- Avg. power for camera flash, ~160mA --> <item name="camera.avg">550</item> <!-- Avg. power use of camera in standard usecases, ~550mA --> <item name="gps.on">50</item> <!-- ~50mA --> <!-- Radio related values. For modems without energy reporting support in firmware, use radio.active, radio.scanning, and radio.on. --> <item name="radio.active">200</item> <!-- ~200mA --> <item name="radio.scanning">10</item> <!-- cellular radio scanning for signal, ~10mA --> <!-- Current consumed by the radio at different signal strengths, when paging --> <array name="radio.on"> <!-- Strength 0 to BINS-1 --> <value>2</value> <!-- ~2mA --> <value>1</value> <!-- ~1mA --> </array> <!-- Radio related values. For modems WITH energy reporting support in firmware, use modem.controller.idle, modem.controller.tx, modem.controller.rx, modem.controller.voltage. --> <item name="modem.controller.idle">0</item> <item name="modem.controller.rx">0</item> <item name="modem.controller.tx">0</item> <item name="modem.controller.voltage">0</item> <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the number of CPU cores for that cluster. Ex: <array name="cpu.clusters.cores"> <value>4</value> // cluster 0 has cpu0, cpu1, cpu2, cpu3 <value>2</value> // cluster 1 has cpu4, cpu5, cpu6, cpu7 </array> --> <array name="cpu.clusters.cores"> <value>4</value> <value>4</value> </array> <!-- Different CPU speeds for cluster 0 as reported in /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state. There must be one of these for each cluster, labeled: cpu.speeds.cluster0, cpu.speeds.cluster1, etc... 注释:带cluster就是区分大小核的细分了--> <array name="cpu.speeds.cluster0"> <value>900000</value> <value>979000</value> <value>1085000</value> <value>1218000</value> <value>1351000</value> <value>1484000</value> <value>1617000</value> <value>1750000</value> <value>1779000</value> <value>1809000</value> <value>1838000</value> <value>1868000</value> <value>1897000</value> <value>1927000</value> <value>1961000</value> <value>2001000</value> </array> <array name="cpu.speeds.cluster1"> <value>900000</value> <value>979000</value> <value>1085000</value> <value>1218000</value> <value>1351000</value> <value>1484000</value> <value>1617000</value> <value>1750000</value> <value>1779000</value> <value>1809000</value> <value>1838000</value> <value>1868000</value> <value>1897000</value> <value>1927000</value> <value>1961000</value> <value>2001000</value> </array> <!-- Current at each CPU speed for cluster 0, as per \'cpu.speeds.cluster0\'. Like cpu.speeds.cluster0, there must be one of these present for each heterogeneous CPU cluster. --> <array name="cpu.active.cluster0"> <value>254.95</value> <value>354.95</value> <value>387.15</value> <value>442.86</value> <value>510.20</value> <value>582.65</value> <value>631.99</value> <value>812.02</value> <value>858.84</value> <value>943.23</value> <value>992.45</value> <value>1086.32</value> <value>1151.96</value> <value>1253.80</value> <value>1397.67</value> <value>1450.67</value> </array> <array name="cpu.active.cluster1"> <value>254.95</value> <value>354.95</value> <value>387.15</value> <value>442.86</value> <value>510.20</value> <value>582.65</value> <value>631.99</value> <value>812.02</value> <value>858.84</value> <value>943.23</value> <value>992.45</value> <value>1086.32</value> <value>1151.96</value> <value>1253.80</value> <value>1397.67</value> <value>1450.67</value> </array> <!-- Current when CPU is idle --> <item name="cpu.idle">10</item> <!-- Memory bandwidth power values in mA at the rail. There must be one value for each bucket defined in the device tree. --> <array name="memory.bandwidths"> <value>22.7</value> <!-- mA for bucket: 100mb/s-1.5 GB/s memory bandwidth --> </array> <!-- This is the battery capacity in mAh (measured at nominal voltage) --> <item name="battery.capacity">3000</item> <!-- Wifi related values. --> <!-- Idle Receive current for wifi radio in mA. 0 by default--> <item name="wifi.controller.idle">10</item> <!-- Rx current for wifi radio in mA. 0 by default--> <item name="wifi.controller.rx">150</item> <!-- Tx current for wifi radio in mA. 0 by default--> <item name="wifi.controller.tx">150</item> <!-- Current at each of the wifi Tx levels in mA. The number of tx levels varies per device and is available only of wifi chipsets which support the tx level reporting. Use wifi.tx for other chipsets. none by default --> <array name="wifi.controller.tx_levels"> <!-- mA --> </array> <!-- Operating volatage for wifi radio in mV. 0 by default--> <item name="wifi.controller.voltage">0</item> <array name="wifi.batchedscan"> <!-- mA --> <value>.2</value> <!-- 1-8/hr --> <value>2</value> <!-- 9-64/hr --> <value>20</value> <!-- 65-512/hr --> <value>200</value> <!-- 513-4,096/hr --> <value>500</value> <!-- 4097-/hr --> </array> </device>
一、 概述
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耗电使用情况,目前该参数为true final 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; } //追踪不同进程的耗电情况以上是关于Android功耗统计算法的主要内容,如果未能解决你的问题,请参考以下文章
Qt低功耗蓝牙系列三(低功耗蓝牙客户端的程序设计纯Android代码)
原创Android 耗电信息统计服务——BatteryStats源码分析