软硬件的耗电量在BatteryStatsHelper.java
中的两个方法中实现
- processAppUsage 软件APP耗电量
- processMiscUsage 硬件耗电量
还是在线网站http://androidxref.com/上对Android版本6.0.1_r10源码进行分析
具体位置在 /frameworks/base/core/java/com/android/internal/os/BatteryStatsHelper.java
本篇大部分来自 https://blog.csdn.net/FightFightFight/article/details/82694381
APP八大耗电模块
八大模块的耗电计算器
计算项 | Class文件 |
---|---|
CPU功耗 | mCpuPowerCalculator.java |
Wakelock功耗 | mWakelockPowerCalculator.java |
无线电功耗 | mMobileRadioPowerCalculator.java |
WIFI功耗 | mWifiPowerCalculator.java |
蓝牙功耗 | mBluetoothPowerCalculator.java |
Sensor功耗 | mSensorPowerCalculator.java |
相机功耗 | mCameraPowerCalculator.java |
闪光灯功耗 | mFlashlightPowerCalculator.java |
硬件耗电模块
硬件功耗的子项分为7项
功耗项 | 解释 |
---|---|
UserUsage | 用户功耗 |
PhoneUsage | 通话功耗 |
ScreenUsage | 屏幕功耗 |
WiFiUsage | Wifi功耗 |
BluetoothUsage | 蓝牙消耗 |
IdleUsage | CPU Idle功耗 |
RadioUsage | 移动无线功耗 |
processAppUsage方法
该方法用来统计软件耗电量,在进行统计时,以uid为单位进行统计,Android中的uid是应用安装时由系统分配的,每个应用对应一个uid,背后也涉及了Android的安全机制。咱们也可以通过清单文件 AndroidManifest.xml
中的shareUserId
指定两个应用的uid相同,用来共享部分数据信息
例如这里就是跟系统同一个uid,即设置为系统应用
对于每个uid,也就是我们的一个APP,都会对应一个BatterySipper
对象,APP计算得到的耗电数据会存储到BatterySipper
对象中
跟进方法查看
是否统计所有用户使用的APP,默认为false
final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
电池对应类型的真实运行时间
mStatsPeriod = mTypeBatteryRealtime;
该值在refreshStats
方法中已经赋值
mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
系统耗电量
BatterySipper osSipper = null;
获取所有应用APP uid并遍历,实例化BatterySipper
对象,存储电量的统计信息
BatterySipper.DrainType.APP
表示统计类型为应用耗电
final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {
final Uid u = uidStats.valueAt(iu);
final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
软件的耗电归根结底还是使用硬件导致的,比如说屏幕,WIFI,CPU等等,所以接下来就是统计APP对应使用的八大硬件的耗电量
// 计算CPU耗电量
mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
// 计算wakelock耗电量,也就是唤醒手机,不让它息屏,比如说我们玩游戏的时候手机不会玩着玩着就黑屏
mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
// 计算无线电耗电量
mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
// 计算WIFI耗电量
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);
APP总耗电量
final double totalPower = app.sumPower();
if (DEBUG && totalPower != 0) {
Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
makemAh(totalPower)));
}
如果统计电量大于零或者为ROOT用户,进入if语句
// Add the app to the list if it is consuming power.
if (totalPower != 0 || u.getUid() == 0) {
//
// Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
// 获取对应APP的进程ID
final int uid = app.getUid();
// 获取用户ID,默认用户ID为0
final int userId = UserHandle.getUserId(uid);
如果当前进程为WIFI,WIFI uid为1010,则添加到统计wifi耗电量的列表List<BatterySipper>
中
if (uid == Process.WIFI_UID) {
mWifiSippers.add(app);
蓝牙添加到蓝牙列表中 uid为1002
} else if (uid == Process.BLUETOOTH_UID) {
mBluetoothSippers.add(app);
当不为多用户并且该userId没有对应的用户时,添加到其他用户中
} else if (!forAllUsers && asUsers.get(userId) == null
&& UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
// We are told to just report this user's apps as one large entry.
List<BatterySipper> list = mUserSippers.get(userId);
if (list == null) {
list = new ArrayList<>();
mUserSippers.put(userId, list);
}
list.add(app);
app耗电都添加到mUsageList
中
} else {
mUsageList.add(app);
}
如果是root进程,也就是操作系统的耗电量,则设置为osSipper
if (uid == 0) {
osSipper = app;
}
当然计算并不一定准确,设备真实的唤醒时间可能比屏幕打开时间和应用程序唤醒时间更长,所以如果还剩下没有分配的电量消耗,将其分配给操作系统
if (osSipper != null) {
// The device has probably been awake for longer than the screen on
// time and application wake lock time would account for. Assign
// this remainder to the OS, if possible.
mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime,
mRawUptime, mStatsType);
osSipper.sumPower();
}
APP对于八大硬件耗电计算
接下来详细分析APP对于八大硬件的耗电量计算
(算了,直接看这篇吧,这部分我暂时还用不到 http://gityuan.com/2016/01/10/power_rank/
1 CPU
2 Wakelock
3 WIFI
4 BlueTooth
5 Camera
6 Flashlight
7 MobileRadio
8 Sensor
processMiscUsage方法
processMiscUsage()
方法来统计硬件耗电量
private void processMiscUsage() {
addUserUsage();
addPhoneUsage();
addScreenUsage();
addWiFiUsage();
addBluetoothUsage();
addIdleUsage(); // Not including cellular idle power
// Don't compute radio usage if it's a wifi-only device
if (!mWifiOnly) {
addRadioUsage();
}
}
计算用户耗电量
addUserUsage();
具体实现如下,将UserSippers中的功耗都合入bs
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");
mUsageList.add(bs);
}
}
aggregateSippers
方法实现如下
private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
for (int i=0; i<from.size(); i++) {
BatterySipper wbs = from.get(i);
if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
bs.add(wbs); // 将from中的功耗数据合入bs
}
bs.computeMobilemspp();
bs.sumPower(); // 计算总功耗
}
累加所有通话耗电情况,统计到DrainType.PHONE
类型的BatterySipper
中
addPhoneUsage();
具体实现为
private void addPhoneUsage() {
// 获取BatteryStatsService中统计的通话总时长
long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;
// 获取mPowerProfile文件中无线电发送/接收信号时消耗的平均电量
double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
* phoneOnTimeMs / (60*60*1000);
if (phoneOnPower != 0) {
// 实例化一个DrainTyep为PHONE类型的BatterySipper,并将phoneOnTimeMs、phoneOnPower进行赋值给BatterySipper
addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
}
}
累加所有亮屏的耗电情况,统计到DrainType.SEREEN
类型的BatterySipper
中
addScreenUsage();
private void addScreenUsage() {
double power = 0;
// 获取BatteryStatsService 中统计的屏幕亮屏时间
long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;
// 屏幕耗电量 = 亮屏时间 * 每毫秒亮屏耗电量
power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
// 屏幕以最高亮度打开时每毫秒消耗的电量
final double screenFullPower =
mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
// 遍历相加所有的屏幕耗电值,存储到power
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;
if (DEBUG && p != 0) {
Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
+ " power=" + makemAh(p / (60 * 60 * 1000)));
}
power += p;
}
// 单位切换为小时
power /= (60*60*1000); // To hours
// 实例化一个DrainType为SCREEN类型的BatterySipper,并将screenOnTimeMs,power进行赋值给BatterySipper
if (power != 0) {
addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
}
}
WIFI耗电量,硬件功耗类型为DrainType.WIFI
addWiFiUsage();
private void addWiFiUsage() {
// 实例化一个DrainType.WIFI的BatterySipper,用来存储WIFI的耗电量
BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
// 计算不属于应用消耗的WIFI耗电
mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
// 收集统计APP耗电时获取的WIFI耗电量和剩余WIFI耗电量到bs中
aggregateSippers(bs, mWifiSippers, "WIFI");
// 将bs添加到mUsageList中
if (bs.totalPowerMah > 0) {
mUsageList.add(bs);
}
}
BlueTooth蓝牙耗电量 DrainTyep.BLUETOOTH
类型
private void addBluetoothUsage() {
// 实例化蓝牙BatterySipper
BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
// 通过BT耗电量计算器计算除应用以外的蓝牙耗电量
mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
mStatsType);
aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
if (bs.totalPowerMah > 0) {
mUsageList.add(bs);
}
}
CPU IDLE耗电量,类型为DrainType.IDLE
addIdleUsage();
private void addIdleUsage() {
long idleTimeMs = (mTypeBatteryRealtime
- mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;
double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
/ (60*60*1000);
if (DEBUG && idlePower != 0) {
Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower));
}
if (idlePower != 0) {
addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
}
}
如果不是仅WIFI设备(比如平板就只有WIFI),咱们还需要计算流量消耗
if (!mWifiOnly) {
addRadioUsage();
}
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);
}
}
软硬件功耗项对比
功耗项 | 软件榜 | 硬件榜 |
---|---|---|
CPU | √ | √ |
无线电 | √ | √ |
WIFI | √ | √ |
蓝牙 | √ | √ |
Wakelock | √ | - |
Sensor | √ | - |
相机 | √ | - |
闪光灯 | √ | - |
通话 | - | √ |
屏幕 | - | √ |
用户 | - | √ |
参考链接
- https://blog.csdn.net/FightFightFight/article/details/82694381
- http://gityuan.com/2016/01/10/power_rank/
- https://duanqz.github.io/2015-07-21-batterystats-part1
- http://androidxref.com/6.0.1_r10/xref//frameworks/base/core/java/com/android/internal/os/BatteryStatsHelper.java
END
建了一个微信的安全交流群,欢迎添加我微信备注进群
,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注