BatteryStatsHelper.java源码分析

在分析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驻留广播,该广播监听系统电池电量和充电状态

mCollectBatteryBroadcast = collectBatteryBroadcast; 

设备是否只有wifi,无移动网络,比如说平板或者车机,有的就是不能插SIM卡的

mWifiOnly = wifiOnly; 

查看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对象

mPowerProfile = new PowerProfile(mContext); 

PowerProfile创建

持续跟进

public PowerProfile(Context context) {     // Read the XML file for the given profile (normally only one per     // device)     if (sPowerMap.size() == 0) {         readPowerValuesFromXml(context);     }     initCpuClusters(); } 

可以看到这里有一段注释: Read the XML file for the given profile (normally only one perdevice

跟进readPowerValuesFromXml方法,其实这个方法就是用来解析power_profile.xml文件的,该文件在源码中的位置为 /frameworks/base/core/res/res/xml/power_profile.xmlpower_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 {         // .... 

BatteryStatsHelper.java源码分析

在这里需要提一下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 系统不包括休眠的运行时间
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,             long rawUptimeUs) { 

初始化Stats操作

getStats() 

如果mStats为空,则初始化

public BatteryStats getStats() {     if (mStats == null) {         load();     }     return mStats; } mMaxPower = 0; // 最大耗电量 mMaxRealPower = 0; // 最大真实耗电量 mComputedPower = 0; // 通过耗电计算器计算的耗电量总和 mTotalPower = 0; // 总的耗电量 

刷新耗电量之前需要先清空之前的数据,clear都是清空操作

mUsageList.clear(); // 存储了BatterySipper列表,各类耗电量都存储在BatterySipper中,BatterySipper存储在mUsageList中 mWifiSippers.clear(); // 在统计软件耗电过程中使用到WIFI的应用,其对应的BatterySipper列表 mBluetoothSippers.clear(); // 在统计软件耗电过程中使用到BlueTooth的应用,其对应的BatterySipper列表 mUserSippers.clear(); // 设备上有多个用户时,存储了其他用户的耗电信息的SparseArray数据,键为userId,值为对应的List<BatterySipper> mMobilemsppList.clear(); // 存储有数据接收和发送的BatterySipper对象的列表 

初始化八大模块的耗电计算器,都继承于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变量来表示

mStatsType = statsType; 

有三种可选值

  // 统计从上一次充电以来至现在的耗电量   public static final int STATS_SINCE_CHARGED = 0;    // 统计系统启动以来到现在的耗电量   public static final int STATS_CURRENT = 1;    // 统计从上一次拔掉USB线以来到现在的耗电量   public static final int STATS_SINCE_UNPLUGGED = 2; 

当前系统的运行时间

mRawUptimeUs = rawUptimeUs; 

当前系统的真实运行时间,包括休眠时间

mRawRealtimeUs = 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); // 电池预计多久充满时长 

DEBUG模式下会输出时间日志,这不重要

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)); } 

计算最低和最高的电量近似值

该方法待会详细说明,现在我们只需要知道它主要进行统计APP软件的耗电量操作,统计之后会将每种类型,每个UID的耗电值存储在对应的BatterySipper

processAppUsage(asUsers); 

对每个应用程序的每毫秒ms接收和发送的数据包mobilemspp进行排序

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);         }     } } 

mMobilemsppList进行排序

Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {     @Override     public int compare(BatterySipper lhs, BatterySipper rhs) {         return Double.compare(rhs.mobilemspp, lhs.mobilemspp);     } }); 

计算硬件的耗电量,跟前面的processAppUsage(asUsers);对应,这两个方法我们都后面再说

processMiscUsage(); 

对软硬件耗电量结果进行降序排序

Collections.sort(mUsageList); 

获取最大耗电量

因为我们刚才进行了排序,所以耗电最多的硬件/软件正位于顶部,赋值mMaxRealPower最大真实耗电量

遍历usageList计算得到mComputedPower耗电量总和

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;     } } 

如果存在未计算到的耗电量,实例化一个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

// 如果最高放电量 < 计算的总耗电量,说明多算了耗电量 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);     } } 

这篇已经太长了,关于软硬件的耗电量计算就在另外一篇里面写吧

参考链接

END

建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃

BatteryStatsHelper.java源码分析 BatteryStatsHelper.java源码分析

发表评论

相关文章

  • 0