BatteryStatsHelper.java源码分析 - 春告鳥
source link: https://www.cnblogs.com/Cl0ud/p/17051836.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
在分析PowerUsageSummary
的时候,其实可以发现主要获取应用和服务电量使用情况的实现是在BatteryStatsHelper.java
中
还是在线网站http://androidxref.com/上对Android版本6.0.1_r10源码进行分析
具体位置在 /frameworks/base/core/java/com/android/internal/os/BatteryStatsHelper.java
create方法
查看构造方法
public BatteryStatsHelper(Context context) { this(context, true); }
public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) { this(context, collectBatteryBroadcast, checkWifiOnly(context)); }
public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) { mContext = context; mCollectBatteryBroadcast = collectBatteryBroadcast; mWifiOnly = wifiOnly; }
设置是否需要注册BATTERY_CHANGED
驻留广播,该广播监听系统电池电量和充电状态
设备是否只有wifi,无移动网络,比如说平板或者车机,有的就是不能插SIM卡的
查看create方法
public void create(BatteryStats stats) { mPowerProfile = new PowerProfile(mContext); mStats = stats; }
public void create(Bundle icicle) { if (icicle != null) { mStats = sStatsXfer; mBatteryBroadcast = sBatteryBroadcastXfer; } mBatteryInfo = IBatteryStats.Stub.asInterface( ServiceManager.getService(BatteryStats.SERVICE_NAME)); mPowerProfile = new PowerProfile(mContext); }
其中都获取了PowerProfile对象
PowerProfile创建
可以看到这里有一段注释: Read the XML file for the given profile (normally only one perdevice
跟进readPowerValuesFromXml
方法,其实这个方法就是用来解析power_profile.xml
文件的,该文件在源码中的位置为 /frameworks/base/core/res/res/xml/power_profile.xml
,power_profile.xml
是一个可配置的功耗数据文件
private void readPowerValuesFromXml(Context context) { int id = com.android.internal.R.xml.power_profile; final Resources resources = context.getResources(); XmlResourceParser parser = resources.getXml(id); boolean parsingArray = false; ArrayList<Double> array = new ArrayList<Double>(); String arrayName = null;
try { // ....
在这里需要提一下Android中对于应用和硬件的耗电量计算方式:
有一张“价格表”,记录每种硬件1秒钟耗多少电。有一张“购物清单”,记录apk使用了哪几种硬件,每种硬件用了多长时间。假设某个应用累计使用了60秒的cpu,cpu1秒钟耗1mAh,那这个应用就消耗了60mAh的电
这里的价格表就是我们找到的power_profile.xml
文件,手机的硬件是各不相同的,所以每一款手机都会有一张自己的"价格表",这张表的准确性由手机厂商负责。
这也是为什么我们碰到读取xml文件的时候注释里面会有normally only one perdevice
如果我们想要看自己手机的power_profile.xml文件咋办,它会存储在手机的/system/framework/framework-res.apk
路径中,我们可以将它pull出来,通过反编译的手法获得power_profile.xml
文件
refreshStats方法
接着可以看到重载的refreshStats
/** * Refreshes the power usage list. */ public void refreshStats(int statsType, int asUser) { SparseArray<UserHandle> users = new SparseArray<>(1); users.put(asUser, new UserHandle(asUser)); refreshStats(statsType, users); }
/** * Refreshes the power usage list. */ public void refreshStats(int statsType, List<UserHandle> asUsers) { final int n = asUsers.size(); SparseArray<UserHandle> users = new SparseArray<>(n); for (int i = 0; i < n; ++i) { UserHandle userHandle = asUsers.get(i); users.put(userHandle.getIdentifier(), userHandle); } refreshStats(statsType, users); }
/** * Refreshes the power usage list. */ public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) { refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000, SystemClock.uptimeMillis() * 1000); }
refreshStats
是刷新电池使用数据的接口,向上提供数据,其中的具体实现在
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs) { // Initialize mStats if necessary. getStats();
mMaxPower = 0; mMaxRealPower = 0; mComputedPower = 0; mTotalPower = 0;
mUsageList.clear(); mWifiSippers.clear(); mBluetoothSippers.clear(); mUserSippers.clear(); mMobilemsppList.clear();
if (mStats == null) { return; }
if (mCpuPowerCalculator == null) { mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); } mCpuPowerCalculator.reset();
if (mWakelockPowerCalculator == null) { mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile); } mWakelockPowerCalculator.reset();
if (mMobileRadioPowerCalculator == null) { mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats); } mMobileRadioPowerCalculator.reset(mStats);
// checkHasWifiPowerReporting can change if we get energy data at a later point, so // always check this field. final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile); if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) { mWifiPowerCalculator = hasWifiPowerReporting ? new WifiPowerCalculator(mPowerProfile) : new WifiPowerEstimator(mPowerProfile); mHasWifiPowerReporting = hasWifiPowerReporting; } mWifiPowerCalculator.reset();
final boolean hasBluetoothPowerReporting = checkHasBluetoothPowerReporting(mStats, mPowerProfile); if (mBluetoothPowerCalculator == null || hasBluetoothPowerReporting != mHasBluetoothPowerReporting) { mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile); mHasBluetoothPowerReporting = hasBluetoothPowerReporting; } mBluetoothPowerCalculator.reset();
if (mSensorPowerCalculator == null) { mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile, (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE)); } mSensorPowerCalculator.reset();
if (mCameraPowerCalculator == null) { mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile); } mCameraPowerCalculator.reset();
if (mFlashlightPowerCalculator == null) { mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile); } mFlashlightPowerCalculator.reset();
mStatsType = statsType; mRawUptime = rawUptimeUs; mRawRealtime = rawRealtimeUs; mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs); mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs); mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType); mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs); mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
if (DEBUG) { Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime=" + (rawUptimeUs/1000)); Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime=" + (mBatteryUptime/1000)); Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime=" + (mTypeBatteryUptime/1000)); } mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100;
processAppUsage(asUsers);
// Before aggregating apps in to users, collect all apps to sort by their ms per packet. for (int i=0; i<mUsageList.size(); i++) { BatterySipper bs = mUsageList.get(i); bs.computeMobilemspp(); if (bs.mobilemspp != 0) { mMobilemsppList.add(bs); } }
for (int i=0; i<mUserSippers.size(); i++) { List<BatterySipper> user = mUserSippers.valueAt(i); for (int j=0; j<user.size(); j++) { BatterySipper bs = user.get(j); bs.computeMobilemspp(); if (bs.mobilemspp != 0) { mMobilemsppList.add(bs); } } } Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() { @Override public int compare(BatterySipper lhs, BatterySipper rhs) { return Double.compare(rhs.mobilemspp, lhs.mobilemspp); } });
processMiscUsage();
Collections.sort(mUsageList);
// At this point, we've sorted the list so we are guaranteed the max values are at the top. // We have only added real powers so far. if (!mUsageList.isEmpty()) { mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah; final int usageListCount = mUsageList.size(); for (int i = 0; i < usageListCount; i++) { mComputedPower += mUsageList.get(i).totalPowerMah; } }
if (DEBUG) { Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge=" + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower)); }
mTotalPower = mComputedPower; if (mStats.getLowDischargeAmountSinceCharge() > 1) { if (mMinDrainedPower > mComputedPower) { double amount = mMinDrainedPower - mComputedPower; mTotalPower = mMinDrainedPower; BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
// Insert the BatterySipper in its sorted position. int index = Collections.binarySearch(mUsageList, bs); if (index < 0) { index = -(index + 1); } mUsageList.add(index, bs); mMaxPower = Math.max(mMaxPower, amount); } else if (mMaxDrainedPower < mComputedPower) { double amount = mComputedPower - mMaxDrainedPower;
// Insert the BatterySipper in its sorted position. BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount); int index = Collections.binarySearch(mUsageList, bs); if (index < 0) { index = -(index + 1); } mUsageList.add(index, bs); mMaxPower = Math.max(mMaxPower, amount); } } }
我们依次分析
SparseArray<UserHandle> asUsers
UserHanler代表设备上的一个用户long rawRealtimeUs
系统开机后的运行时间long rawUptimeUs
系统不包括休眠的运行时间
初始化Stats操作
如果mStats为空,则初始化
刷新耗电量之前需要先清空之前的数据,clear都是清空操作
初始化八大模块的耗电计算器,都继承于PowerCalculator
抽象类,八大模块在processAppUsage
方法中进行分析,这里只需要知道有哪八个以及进行的操作是初始化即可
计算项 | Class文件 |
---|---|
CPU功耗 | mCpuPowerCalculator.java |
Wakelock功耗 | mWakelockPowerCalculator.java |
无线电功耗 | mMobileRadioPowerCalculator.java |
WIFI功耗 | mWifiPowerCalculator.java |
蓝牙功耗 | mBluetoothPowerCalculator.java |
Sensor功耗 | mSensorPowerCalculator.java |
相机功耗 | mCameraPowerCalculator.java |
闪光灯功耗 | mFlashlightPowerCalculator.java |
if (mCpuPowerCalculator == null) { mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); } mCpuPowerCalculator.reset();
if (mWakelockPowerCalculator == null) { mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile); } mWakelockPowerCalculator.reset();
if (mMobileRadioPowerCalculator == null) { mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats); } mMobileRadioPowerCalculator.reset(mStats);
// checkHasWifiPowerReporting can change if we get energy data at a later point, so // always check this field. final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile); if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) { mWifiPowerCalculator = hasWifiPowerReporting ? new WifiPowerCalculator(mPowerProfile) : new WifiPowerEstimator(mPowerProfile); mHasWifiPowerReporting = hasWifiPowerReporting; } mWifiPowerCalculator.reset();
final boolean hasBluetoothPowerReporting = checkHasBluetoothPowerReporting(mStats, mPowerProfile); if (mBluetoothPowerCalculator == null || hasBluetoothPowerReporting != mHasBluetoothPowerReporting) { mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile); mHasBluetoothPowerReporting = hasBluetoothPowerReporting; } mBluetoothPowerCalculator.reset();
if (mSensorPowerCalculator == null) { mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile, (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE)); } mSensorPowerCalculator.reset();
if (mCameraPowerCalculator == null) { mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile); } mCameraPowerCalculator.reset();
if (mFlashlightPowerCalculator == null) { mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile); } mFlashlightPowerCalculator.reset();
电量统计需要先设置统计时间段,通过设置统计类型mStatsType变量来表示
有三种可选值
// 统计从上一次充电以来至现在的耗电量 public static final int STATS_SINCE_CHARGED = 0;
// 统计系统启动以来到现在的耗电量 public static final int STATS_CURRENT = 1;
// 统计从上一次拔掉USB线以来到现在的耗电量 public static final int STATS_SINCE_UNPLUGGED = 2;
当前系统的运行时间
当前系统的真实运行时间,包括休眠时间
剩下的也是一堆时间
DEBUG模式下会输出时间日志,这不重要
计算最低和最高的电量近似值
该方法待会详细说明,现在我们只需要知道它主要进行统计APP软件的耗电量操作,统计之后会将每种类型,每个UID的耗电值存储在对应的BatterySipper
中
对每个应用程序的每毫秒ms接收和发送的数据包mobilemspp
进行排序
对mMobilemsppList
进行排序
计算硬件的耗电量,跟前面的processAppUsage(asUsers);
对应,这两个方法我们都后面再说
对软硬件耗电量结果进行降序排序
获取最大耗电量
因为我们刚才进行了排序,所以耗电最多的硬件/软件正位于顶部,赋值mMaxRealPower
最大真实耗电量
遍历usageList
计算得到mComputedPower
耗电量总和
如果存在未计算到的耗电量,实例化一个DrainType.UNACCOUNTED
类型的BatterySipper
进行存储,并添加到mUsageList
中
mTotalPower = mComputedPower; if (mStats.getLowDischargeAmountSinceCharge() > 1) { // 如果最低放电量 > 计算的总耗电量,说明还有未计算的 if (mMinDrainedPower > mComputedPower) { double amount = mMinDrainedPower - mComputedPower; mTotalPower = mMinDrainedPower; // 实例化一个DrainType.UNACCOUNTED类型的BatterySipper,用来存储未计算的耗电量 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
// Insert the BatterySipper in its sorted position. int index = Collections.binarySearch(mUsageList, bs); if (index < 0) { index = -(index + 1); } mUsageList.add(index, bs); mMaxPower = Math.max(mMaxPower, amount); }
如果存在计算多了的耗电量,实例化一个DrainType.OVERCOUNTED
类型的BatterySipper
进行存储,并添加到mUsageList
中
这篇已经太长了,关于软硬件的耗电量计算就在另外一篇里面写吧
建了一个微信的安全交流群,欢迎添加我微信备注进群
,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃
__EOF__
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK