BatteryStatsService主要负责电池电量的统计信息,首先我们简单的看下电量统计服务的启动过程。
BatteryStatsService启动过程
从BatteryStatsService的启动时序图可以看出,BatteryStatsService服务是在ActivityManagerService服务中启动的
1. 在SystemServer中startBootstrapServices()方法中创建了ActivityManagerService服务的对象,并调用了SystemServiceManager的startService()方法启动了BatteryStatsService服务
首先分析BatteryStatsService的构造方法
- mStats = new BatteryStatsImpl(systemDir, handler, mHandler);
mStats = new BatteryStatsImpl(systemDir, handler, mHandler);
构造方法中创建了一个BatterystatsImpl对象,BatteryStatsService真正的处理逻辑其实都是在BatteryStatsImpl类中。
在BatteryStatsImpl类的构造函数中首先创建了一个文件
- if (systemDir != null) {
- mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
- new File(systemDir, "batterystats.bin.tmp"));
- } else {
- mFile = null;
- }
if (systemDir != null) {mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),new File(systemDir, "batterystats.bin.tmp"));} else {mFile = null;}
在手机的data/system/目录下创建一个batterystats.bin文件
该文件用于保存BatteryStats电量的统计信息,系统会不定时的将电量统计信息BatteryStats写入到文件中。
然后在构造函数中初始化了mScreenOnTimer,mPhoneOnTimer,
WifiOnTimer等一系列的StopWatchTimer,这些Timer主要用于统计各种模块耗电的时间。
2.在ActivityManagerService服务的构造方法中创建了BatteryStatsService服务的对象,调用了readLocked()方法读取电池统计信息,初始化BatteryStats
3.调用BatteryStatsService的initPwerManagerment()方法初始化电量统计服务,将BatteryStatsService服务注册到ServiceManager中
收集、存储电量统计信息
收集有很多的硬件模块,CPU、显示屏、WiFi、蓝牙、闪光灯等,在Android系统中,手机的电压是一定的,所以手机的电量计算就是模块使用时间*模块耗电。
其中关键就是模块使用时间的统计。电量信息的收集在内存中是有BatteryStats来描述的,由于收集的电量会从不同的维度来计算,所以该类设计的比较复杂。
电量收集的关系图。
在BatterystatsService中有很多note*** 方法,这些方法就是 其他的模块调用用来统计相关的电量使用时间的。比如在PMS类中经常会有调用Notifier类相关的方法来进行通知,这些方法最终都会调用到BatteryStatsService中note***方法来通知该服务统计电量信息
通过其中一个时间收集的例子来学习下模块电量时间收集的过程。
比如使用闪光灯的时间收集过程。
在 /frameworks/av/services/camera/libcameraservice/CameraService.cpp类中。
使用闪光灯的时候会调用notifier.noteFlashlightOn(cameraId, newUid)
方法,当停止使用闪光灯的时候会调用notifier.noteFlashlightOff(cameraId, oldUid)方法。
首先我们看下noteFlashLigntOn方法,该方法最终调用BatteryStatsImpl类中的noteFlashLightOnLocked方法来处理
- public void noteFlashlightOnLocked(int uid) {
- //获取当前调用应用程序的Uid
- uid = mapUid(uid);
- //获取真实系统时间
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- //获取系统运行时间
- final long uptime = SystemClock.uptimeMillis();
- if (mFlashlightOnNesting++ == 0) {
- ……
- addHistoryRecordLocked(elapsedRealtime, uptime);
- //调用Timer开始计时
- mFlashlightOnTimer.startRunningLocked(elapsedRealtime);
- }
- //根据UiD来统计该应用中flash的使用时间
- getUidStatsLocked(uid).noteFlashlightTurnedOnLocked(elapsedRealtime);
- }
public void noteFlashlightOnLocked(int uid) {//获取当前调用应用程序的Uiduid = mapUid(uid);//获取真实系统时间final long elapsedRealtime = SystemClock.elapsedRealtime();//获取系统运行时间final long uptime = SystemClock.uptimeMillis();if (mFlashlightOnNesting++ == 0) {
……addHistoryRecordLocked(elapsedRealtime, uptime);//调用Timer开始计时mFlashlightOnTimer.startRunningLocked(elapsedRealtime);}//根据UiD来统计该应用中flash的使用时间getUidStatsLocked(uid).noteFlashlightTurnedOnLocked(elapsedRealtime);}
该方法中会调用Timer开始统计时间,同时根据UID来统计该每个应用使用Flash的时间。这边有两中Timer,一种是总体的Timer,用来统计该模块总共的使用时长,另一种在每个应用相关的UID中也有Timer,用来统计每个应用使用该模块的时长
我们看下Timer的startRunningLocked方法,看看如何开始统计使用时间。
- void startRunningLocked(long elapsedRealtimeMs) {
- if (mNesting++ == 0) {
- //计算当前的batteryRealTime时间
- final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
- //根据当前时间记录更新时间mUpdateTime
- mUpdateTime = batteryRealtime;
- ……
- mCount++;
- mAcquireTime = mTotalTime;
- }
- }
void startRunningLocked(long elapsedRealtimeMs) {if (mNesting++ == 0) {//计算当前的batteryRealTime时间final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);//根据当前时间记录更新时间mUpdateTimemUpdateTime = batteryRealtime;……mCount++;mAcquireTime = mTotalTime;}}
该方法其实也是比较简单,在调用该方法的时候记录下更新时间mUpdateTime
noteFlashLightOff方法同样是调用Timer停止统计FlashLight的使用时间,同时按UID停止统计调用应用程序的使用时间。
Timer的stopRunningLocked方法
- void stopRunningLocked(long elapsedRealtimeMs) {
- if (mNesting == 0) {
- return;
- }
- if (--mNesting == 0) {
- //计算现在的batteryRealTime时间
- final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
- ……
- mNesting = 1;
- //计算该Timer统计的已经使用总时长totalTime
- mTotalTime = computeRunTimeLocked(batteryRealtime);
- mNesting = 0;
- ……
- }
- }
void stopRunningLocked(long elapsedRealtimeMs) {if (mNesting == 0) {return;}if (--mNesting == 0) {//计算现在的batteryRealTime时间final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);……mNesting = 1;//计算该Timer统计的已经使用总时长totalTimemTotalTime = computeRunTimeLocked(batteryRealtime);mNesting = 0;……}}
该方法中在停止统计的时候,根据batteryRealTime计算出该Timer统计的已经使用的总时长
结束当前的时间 – 开始的时间 = 模块使用时长
computeRunTimeLocked计算 总时长 = 总时长 + (结束当前的时间 – 开始的时间)
- return mTotalTime + (batteryRealTime - mUpdateTime)
return mTotalTime + (batteryRealTime - mUpdateTime)
到此为止可以看出,统计各个模块的使用时间实际就是 当该模块使用或者结束的时候,调用相应的方法开始统计和结束统计,该时间段的使用时间就是结束的时间 – 开始的时间。该模块的使用的总时间就是各个使用时间段的总和。
各个模块的使用时间统计最终保存在BatteryStats类中的各个timer中。
电量计算
计算耗电量离不开BatteryStatsHelper这个类,在Settings设置中显示电量相关信息就是调用BatteryStatsHelper的接口来实现的。
BatteryStatsHelper主要作用是用来计算所有应用和服务的用电量信息,使用BatteryStatsHelper接口的时候,必须在Activity或者Fragment初始化的时候调用BatteryStatsHelper的create()来初始化它,同时在activity或者Fragment销毁的时候调用destroy()方法来销毁它。
首先我们看下BatteryStatsHelper的create()初始化方法。
- public void create(Bundle icicle) {
- mBatteryInfo = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
- mPowerProfile = new PowerProfile(mContext);
- }
public void create(Bundle icicle) {mBatteryInfo = IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME));mPowerProfile = new PowerProfile(mContext);}
Create()方法中主要做了两件事:
1.获取了一个BatteryStatsService服务的对象。用于和BatteryStatsService进行通信。
2.创建了一个PowerProfile文件对象。我们知道Android系统中关于电量计算公式:耗电量 = 模块使用时间 * 单位时间内的耗电量
模块使用时间我们已经在BatteryStatsService中统计过了,那单位时间的耗电量其实就是在PowerProfile文件定义好了,每个手机的power_profile文件都不一样,需要厂商自己定义,只需要根据各个厂商定义的这个文件来计算相关的耗电量即可。
- public PowerProfile(Context context) {
- if (sPowerMap.size() == 0) {
- readPowerValuesFromXml(context);
- }
- }
- 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 {
- XmlUtils.beginDocument(parser, TAG_DEVICE);
- ……
public PowerProfile(Context context) {if (sPowerMap.size() == 0) {readPowerValuesFromXml(context);}}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 {XmlUtils.beginDocument(parser, TAG_DEVICE);……
PowerProfile类初始化的时候,直接读取power_profile的xml文件,然后将预定义的值保存到MAP中。
Power_profile.xml文件
- <device name="Android">
- <item name="none">0</item>
- <item name="screen.on">0.1</item> <!-- ~200mA -->
- <item name="screen.full">0.1</item> <!-- ~300mA -->
- <item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA -->
- <item name="bluetooth.on">0.1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
- <item name="wifi.on">0.1</item> <!-- ~3mA -->
- <item name="wifi.active">0.1</item> <!-- WIFI data transfer, ~200mA -->
- <item name="wifi.scan">0.1</item> <!-- WIFI network scanning, ~100mA -->
- <item name="dsp.audio">0.1</item> <!-- ~10mA -->
- <item name="dsp.video">0.1</item> <!-- ~50mA -->
- ……
<device name="Android"><item name="none">0</item><item name="screen.on">0.1</item> <!-- ~200mA --><item name="screen.full">0.1</item> <!-- ~300mA --><item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA --><item name="bluetooth.on">0.1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA --><item name="wifi.on">0.1</item> <!-- ~3mA --><item name="wifi.active">0.1</item> <!-- WIFI data transfer, ~200mA --><item name="wifi.scan">0.1</item> <!-- WIFI network scanning, ~100mA --><item name="dsp.audio">0.1</item> <!-- ~10mA --><item name="dsp.video">0.1</item> <!-- ~50mA -->……
PowerProfile文件中定义了各个模块在不同状态下的耗电量,由于各个硬件模块的耗电量不相同,所以这个文件是由手机的硬件厂商来定制的。
BatteryStatsHelper的refreshStats()方法用来刷新计算的用电量信息,是BatteryStatsHelper类的关键接口,电量计算也是在该方法在中进行处理的。
- public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
- long rawUptimeUs) {
- //初始化基本的状态
- mMaxPower = 0;
- mMaxRealPower = 0;
- mComputedPower = 0;
- mTotalPower = 0;
- //重置相关的列表
- ……
- //初始化相关的电量计算工具,各种计算工具都继承自PowerCalculator类,用于计算不同模块的耗电量
- if (mCpuPowerCalculator == null) {
- mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
- }
- mCpuPowerCalculator.reset();
- ……
- if (mFlashlightPowerCalculator == null) {
- mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);
- }
- mFlashlightPowerCalculator.reset();
- //初始化相关的信息
- ……
- //获取电池剩余时间
- mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
- //获取充电剩余时间
- mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
- ……
- //计算各个应用的耗电量
- processAppUsage(asUsers);
- ……
- //计算其他杂项的耗电量
- processMiscUsage();
- Collections.sort(mUsageList);
- //计算耗电量最大的应用,并计算所有的应用的总耗电量
- 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;
- }
- }
- 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);
- }
- }
- }
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,long rawUptimeUs) {//初始化基本的状态mMaxPower = 0;mMaxRealPower = 0;mComputedPower = 0;mTotalPower = 0;//重置相关的列表……//初始化相关的电量计算工具,各种计算工具都继承自PowerCalculator类,用于计算不同模块的耗电量if (mCpuPowerCalculator == null) {mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);}mCpuPowerCalculator.reset();……if (mFlashlightPowerCalculator == null) {mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);}mFlashlightPowerCalculator.reset();//初始化相关的信息……//获取电池剩余时间mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);//获取充电剩余时间mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);……//计算各个应用的耗电量processAppUsage(asUsers);……//计算其他杂项的耗电量processMiscUsage();Collections.sort(mUsageList);//计算耗电量最大的应用,并计算所有的应用的总耗电量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;}}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);}}}
这个方法主要逻辑就是来刷新电量统计,重新计算各个应用各个模块的电量。首先就重置了相关的基本变量,初始化了相关的电量计算工具。
最重要的是使用processAppUsage()方法来计算各个应用的耗电量。
- private void processAppUsage(SparseArray<UserHandle> asUsers) {
- final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
- mStatsPeriod = mTypeBatteryRealtime;
- BatterySipper osSipper = null;
- 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);
- //计算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);
- //计算Camera的耗电量
- mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
- //计算闪光灯的耗电量
- mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
- //计算这个应用耗电量的总和
- final double totalPower = app.sumPower();
- if (totalPower != 0 || u.getUid() == 0) {
- //根据应用的类型,将应用耗电量信息添加到对应的列表
- final int uid = app.getUid();
- final int userId = UserHandle.getUserId(uid);
- if (uid == Process.WIFI_UID) {
- mWifiSippers.add(app);
- } else if (uid == Process.BLUETOOTH_UID) {
- mBluetoothSippers.add(app);
- } else if (!forAllUsers && asUsers.get(userId) == null
- && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
- //如果该应用是单独某个用户下的应用则添加的userSippers列表中
- if (list == null) {
- list = new ArrayList<>();
- mUserSippers.put(userId, list);
- }
- list.add(app);
- } else {
- //否则直接添加到普通的应用列表
- mUsageList.add(app);
- }
- if (uid == 0) {
- //如果应用id为0,则这个应用为android系统
- osSipper = app;
- }
- }
- }
- if (osSipper != null) {
- mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime,
- mRawUptime, mStatsType);
- osSipper.sumPower();
- }
- }
private void processAppUsage(SparseArray<UserHandle> asUsers) {final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);mStatsPeriod = mTypeBatteryRealtime;BatterySipper osSipper = null;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);//计算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);//计算Camera的耗电量mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算闪光灯的耗电量mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);//计算这个应用耗电量的总和final double totalPower = app.sumPower();if (totalPower != 0 || u.getUid() == 0) {//根据应用的类型,将应用耗电量信息添加到对应的列表final int uid = app.getUid();final int userId = UserHandle.getUserId(uid);if (uid == Process.WIFI_UID) {mWifiSippers.add(app);} else if (uid == Process.BLUETOOTH_UID) {mBluetoothSippers.add(app);} else if (!forAllUsers && asUsers.get(userId) == null&& UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {//如果该应用是单独某个用户下的应用则添加的userSippers列表中if (list == null) {list = new ArrayList<>();mUserSippers.put(userId, list);}list.add(app);} else {//否则直接添加到普通的应用列表mUsageList.add(app);}if (uid == 0) {//如果应用id为0,则这个应用为android系统osSipper = app;}}}if (osSipper != null) {mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime,mRawUptime, mStatsType);osSipper.sumPower();}}
processAppUsage()方法中利用循环的方法,一次计算每一个应用的耗电量。
1. 根据应用的uid来计算该应用的CPU,蓝牙,wifi,手机射频,传感器,camera等硬件的耗电量,所计算的耗电量信息都存储在了BatterySipper这个对象中。
2. 将各个硬件模块的耗电量相加计算出该应用的总耗电量
3. 根据应用的类型,将应用的耗电信息添加的不同的列表归类。其中若uid==0表示当前应用为系统,然后计算出系统的耗电量
那耗电量到底是怎么计算的呢?我们看其中一个电量计算工具的实现方法
WiFiPowerCalculator
- public WifiPowerCalculator(PowerProfile profile) {
- //获取WIFI三种模式下单位时间的耗电量
- mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
- mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
- mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
- }
- @Override
- public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- //获取WiFi三种模式,每种模式的使用时长
- 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);
- //该应用的WiFi使用总时长
- 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);
- }
public WifiPowerCalculator(PowerProfile profile) {//获取WIFI三种模式下单位时间的耗电量mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);}@Overridepublic void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,long rawUptimeUs, int statsType) {//获取WiFi三种模式,每种模式的使用时长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);//该应用的WiFi使用总时长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);}
可以看出在构造方法中,首先从PowerProfile中来获取预定义的WiFi单位时间的耗电量,在计算app耗电量calculateApp()方法中,获取该应用WiFi三种模式的使用时长,将三种模式的使用时长相加得出该应用使用WiFI的总时长,耗电量就是 app.wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))/ (1000*60*60);
WiFi相应模式的使用时长 * 相应模式的单位时间耗电量。
最后还计算不同模式下的传输的数据包。
从代码实现中可以看出电量计算公式: 耗电量 = 耗电时长 * 单位时间耗电量
其他模块的耗电量和WiFi耗电量计算类似,不再一一分析。
接着看refreshStats()方法。
当计算完各个应用的耗电量后调用processMiscUsage()方法,根据方法名称就可以看出该方法主要用来计算各种杂项的耗电量
- private void processMiscUsage() {
- addUserUsage();
- addPhoneUsage();
- addScreenUsage();
- addWiFiUsage();
- addBluetoothUsage();
- addIdleUsage(); // Not including cellular idle power
- // 如果手机只是支持wifi,就不在计算射频的电量
- if (!mWifiOnly) {
- addRadioUsage();
- }
- }
private void processMiscUsage() {addUserUsage();addPhoneUsage();addScreenUsage();addWiFiUsage();addBluetoothUsage();addIdleUsage(); // Not including cellular idle power// 如果手机只是支持wifi,就不在计算射频的电量if (!mWifiOnly) {addRadioUsage();}}
该方法中主要用来计算那些WiFi, Phohe,Bluetooth等用电量,并将该用电信息添加到用电列表,以WiFi为例,计算WiFi的耗电量:WIFI模块的整体耗电量减去App中WiFI的耗电量
- private void addWiFiUsage() {
- BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
- 计算WiFi的耗电量
- mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
- 将之前WiFi类型的应用电量相加
- aggregateSippers(bs, mWifiSippers, "WIFI");
- if (bs.totalPowerMah > 0) {
- mUsageList.add(bs);
- }
- }
private void addWiFiUsage() {BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);计算WiFi的耗电量mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);将之前WiFi类型的应用电量相加aggregateSippers(bs, mWifiSippers, "WIFI");if (bs.totalPowerMah > 0) {mUsageList.add(bs);}}
在计算应用的耗电量的时候,我们会为每个应用计算相应模块WiFI的使用时长,同时我们也会统计WiFi模块的整体使用时长,两者之间的耗电量会有差距,我们将这部分差值就是WiFi耗电量
- public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- //获取WiFi各种模式下使用总时长
- 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;
- //获取WiFi模块消耗的总电量
- double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
- statsType) / (double)(1000*60*60);
- if (powerDrainMah == 0) {
- //如果WiFi模块消耗的总电量=0;重新计算WiFI消耗总电量.
- powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
- + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
- }
- //求出WiFI消耗总电量 – 各个应用wifi消耗总电量
- app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
- }
public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,long rawUptimeUs, int statsType) {//获取WiFi各种模式下使用总时长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;//获取WiFi模块消耗的总电量double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,statsType) / (double)(1000*60*60);if (powerDrainMah == 0) {//如果WiFi模块消耗的总电量=0;重新计算WiFI消耗总电量.powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)+ (rxTimeMs * mRxCurrentMa)) / (1000*60*60);}//求出WiFI消耗总电量 – 各个应用wifi消耗总电量app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);}
这个方法中,求出了应用中WiFI模块使用总电量和Wifi消耗总电量之间的差值,属于没有应用认领的消耗的电量。
然后调用aggregateSippers()方法将我们以前统计的WIFI类型的应用耗电量相加,计算WiFi的耗电量。添加到usageList列表中。
其他屏幕,蓝牙耗电量类似。
然后再次回到refreshStats()方法中
调用Collection.sort()将电量使用排序,找到消耗电量最多的应用
将usageList列表中各个应用的耗电量相加得出统计的总耗电量,最后根据统计的总耗电量和实际的总耗电量之间的差距,作为未统计的类型,加入到耗电量的列表中
到此为止我们对于手机电量的统计与计算就分析完了
总结:
电量统计:实际使用StopWatchTImer来统计不同模块的使用时长,调用note**startLocked和note**stopLocked方法来开始和停止统计。
电量计算:根据统计的各个模块的使用时长 * 各个模块的单位时间的耗电量得出电量消耗。