BatteryStatsHelper这个类主要是统计各个应用,多用户的每个用户,以及蓝牙,屏幕等耗电统计。
一般使用BatteryStatsHelper这个类,先要new一个实例,然后再调用create函数:
下面我们就先从create分析,两种create方法,其中sStatsXfer是静态的
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);
}
mPowerProfile是从power_profile.xml读取的各个器件的电源消耗参数,文件如下:
<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">100</item> <!-- ~200mA -->
<item name="screen.full">200</item> <!-- ~300mA -->
<item name="bluetooth.active">90.5</item> <!-- Bluetooth data transfer, ~10mA -->
<item name="bluetooth.on">2.5</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
<item name="wifi.on">1.25</item> <!-- ~3mA -->
<item name="wifi.active">130</item> <!-- WIFI data transfer, ~200mA -->
<item name="wifi.scan">100</item> <!-- WIFI network scanning, ~100mA -->
<item name="dsp.audio">30.5</item> <!-- ~10mA -->
<item name="dsp.video">72.5</item> <!-- ~50mA -->
<item name="radio.active">135</item> <!-- ~200mA -->
<item name="radio.scanning">5.3</item> <!-- cellular radio scanning for signal, ~10mA -->
<item name="gps.on">30</item> <!-- ~50mA -->
<!-- Current consumed by the radio at different signal strengths, when paging -->
<array name="radio.on"> <!-- Strength 0 to BINS-1 -->
<value>3.5</value> <!-- ~2mA -->
<value>2.4</value> <!-- ~1mA -->
</array>
<!-- Different CPU speeds as reported in
/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state -->
<array name="cpu.speeds">
<value>624000</value> <!-- 624 MHz CPU speed -->
<value>699563</value> <!-- 699 MHz CPU speed -->
<value>799500</value> <!-- 799 MHz CPU speed -->
<value>899438</value> <!-- 899 MHz CPU speed -->
<value>999375</value> <!-- 999 MHz CPU speed -->
<value>1099313</value> <!-- 1099 MHz CPU speed -->
<value>1199250</value> <!-- 1199 MHz CPU speed -->
<value>1299188</value> <!-- 1299 MHz CPU speed -->
<value>1399125</value> <!-- 1399 MHz CPU speed -->
<value>1499063</value> <!-- 1499 MHz CPU speed -->
<value>1599000</value> <!-- 1599 MHz CPU speed -->
</array>
<!-- Current when CPU is idle -->
<item name="cpu.idle">2.2</item>
<!-- Current at each CPU speed, as per 'cpu.speeds' -->
<array name="cpu.active">//各个cpu频段的功耗
<value>54</value>
<value>63</value>
<value>72</value>
<value>80</value>
<value>90</value>
<value>100</value>
<value>109</value>
<value>115</value>
<value>121</value>
<value>127</value>
<value>135</value>
</array>
<!-- This is the battery capacity in mAh (measured at nominal voltage) -->
<item name="battery.capacity">2000</item>
</device>
接下来说下refreshStats是用来更新电池最新状态的,statsType是指充电状态还是非充电状态,asUsers指的是userId(多用户)
public void refreshStats(int statsType, List<UserHandle> asUsers) {
final int n = asUsers.size();
SparseArray<UserHandle> users = new SparseArray<UserHandle>(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;
mWifiPower = 0;
mBluetoothPower = 0;
mAppMobileActive = 0;
mAppWifiRunning = 0;
mUsageList.clear();
mWifiSippers.clear();
mBluetoothSippers.clear();
mUserSippers.clear();
mUserPower.clear();
mMobilemsppList.clear();
if (mStats == null) {
return;
}
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);//计算每个uid的耗电情况
// 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) {
if (lhs.mobilemspp < rhs.mobilemspp) {
return 1;
} else if (lhs.mobilemspp > rhs.mobilemspp) {
return -1;
}
return 0;
}
});
processMiscUsage();//计算比如屏幕、wifi、蓝牙等耗电
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;
addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);//加一个未统计电量
} else if (mMaxDrainedPower < mComputedPower) {//
double amount = mComputedPower - mMaxDrainedPower;
addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);//加一个over统计
}
}
Collections.sort(mUsageList);
}
下面先看processAppUsage函数,这个函数是看uid的耗电信息。
private void processAppUsage(SparseArray<UserHandle> asUsers) {
final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
............
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {//遍历各个uid
Uid u = uidStats.valueAt(iu);
double p; // in mAs
double power = 0; // in mAs
double highestDrain = 0;
String packageWithHighestDrain = null;
Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
long cpuTime = 0;
long cpuFgTime = 0;
long wakelockTime = 0;
long gpsTime = 0;
..............
if (cpuFgTime > cpuTime) {//先就算每个uid的cpu耗电
if (DEBUG && cpuFgTime > cpuTime + 10000) {
Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
}
cpuTime = cpuFgTime; // Statistics may not have been gathered yet.
}
power /= (60*60*1000);
// Process wake lock usage
Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();
for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
: wakelockStats.entrySet()) {
Uid.Wakelock wakelock = wakelockEntry.getValue();
// Only care about partial wake locks since full wake locks
// are canceled when the user turns the screen off.
BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
if (timer != null) {
wakelockTime += timer.getTotalTimeLocked(mRawRealtime, which);
}
}
appWakelockTimeUs += wakelockTime;
wakelockTime /= 1000; // convert to millis
// Add cost of holding a wake lock//计算uid的wakelock耗电
p = (wakelockTime
* mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000);
if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake "
+ wakelockTime + " power=" + makemAh(p));
power += p;
// Add cost of mobile traffic//计算uid移动数据耗电
...................
if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
+ (mobileRx+mobileTx) + " active time " + mobileActive
+ " power=" + makemAh(p));
power += p;
// Add cost of wifi traffic//计算wifi数据耗电
final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);
final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);
final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);
final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);
p = (wifiRx + wifiTx) * wifiPowerPerPacket;
if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets "
+ (mobileRx+mobileTx) + " power=" + makemAh(p));
power += p;
// Add cost of keeping WIFI running.
long wifiRunningTimeMs = u.getWifiRunningTime(mRawRealtime, which) / 1000;
mAppWifiRunning += wifiRunningTimeMs;
p = (wifiRunningTimeMs
* mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);
if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running "
+ wifiRunningTimeMs + " power=" + makemAh(p));
power += p;
// Add cost of WIFI scans
long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000;
p = (wifiScanTimeMs
* mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000);
if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs
+ " power=" + makemAh(p));
power += p;
for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000;
p = ((batchScanTimeMs
* mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))
) / (60*60*1000);
if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin
+ " time=" + batchScanTimeMs + " power=" + makemAh(p));
power += p;
}
// Process Sensor usage//uid传感器耗电
...................
p = (multiplier * sensorTime) / (60*60*1000);
if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle
+ " time=" + sensorTime + " power=" + makemAh(p));
power += p;
}
if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s",
u.getUid(), makemAh(power)));
// Add the app to the list if it is consuming power
final int userId = UserHandle.getUserId(u.getUid());
if (power != 0 || u.getUid() == 0) {//新建一个BatterySipper,BatterySipper 就是耗电的各个参数
BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u,
new double[] {power});
app.cpuTime = cpuTime;
app.gpsTime = gpsTime;
app.wifiRunningTime = wifiRunningTimeMs;
app.cpuFgTime = cpuFgTime;
app.wakeLockTime = wakelockTime;
app.mobileRxPackets = mobileRx;
app.mobileTxPackets = mobileTx;
app.mobileActive = mobileActive / 1000;
app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType);
app.wifiRxPackets = wifiRx;
app.wifiTxPackets = wifiTx;
app.mobileRxBytes = mobileRxB;
app.mobileTxBytes = mobileTxB;
app.wifiRxBytes = wifiRxB;
app.wifiTxBytes = wifiTxB;
app.packageWithHighestDrain = packageWithHighestDrain;
if (u.getUid() == Process.WIFI_UID) {//如果uid是wifi的,就加到mWifiSippers
mWifiSippers.add(app);
mWifiPower += power;
} else if (u.getUid() == Process.BLUETOOTH_UID) {//蓝牙的就加到mBluetoothSippers
mBluetoothSippers.add(app);
mBluetoothPower += power;
} else if (!forAllUsers && asUsers.get(userId) == null//如果没有这个userId(多用户),就重新建立一个
&& UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) {
List<BatterySipper> list = mUserSippers.get(userId);
if (list == null) {
list = new ArrayList<BatterySipper>();
mUserSippers.put(userId, list);
}
list.add(app);
if (power != 0) {
Double userPower = mUserPower.get(userId);
if (userPower == null) {
userPower = power;
} else {
userPower += power;
}
mUserPower.put(userId, userPower);
}
} else {//或者直接加到mUsageList,mUsageList管理所有的耗电信息list
mUsageList.add(app);
if (power > mMaxPower) mMaxPower = power;
if (power > mMaxRealPower) mMaxRealPower = power;
mComputedPower += power;
}
if (u.getUid() == 0) {
osApp = app;
}
}
}
processMiscUsage函数是统计屏幕、wifi耗电等等
private void processMiscUsage() {
addUserUsage();
addPhoneUsage();
addScreenUsage();
addFlashlightUsage();
addWiFiUsage();
addBluetoothUsage();
addIdleUsage(); // Not including cellular idle power
// Don't compute radio usage if it's a wifi-only device
if (!mWifiOnly) {
addRadioUsage();
}
}
private void addScreenUsage() {
double power = 0;
long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;
power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);//时间乘以平均功耗
final double screenFullPower =
mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
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最后再要除以1小时
if (power != 0) {
addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
}
}
addEntry函数,新建一个BatterySipper,加入mUsageList
private BatterySipper addEntry(DrainType drainType, long time, double power) {
mComputedPower += power;//计算所有的power
if (power > mMaxRealPower) mMaxRealPower = power;
return addEntryNoTotal(drainType, time, power);
}
private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {
if (power > mMaxPower) mMaxPower = power;
BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});//新建一个BatterySipper,加入mUsageList
bs.usageTime = time;
mUsageList.add(bs);
return bs;
}
而如果应用想要获取各个耗电信息,先要refreshStats,再去获取getUsageList,下面是settings中电池那一栏里面的代码
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles);
final List<BatterySipper> usageList = mStatsHelper.getUsageList();
获取后可以根据BatterySipper的value获取每小时的耗电量.
下面我们分析下settings,电池里面的refreshStats函数:
private void refreshStats() {
mAppListGroup.removeAll();
mAppListGroup.setOrderingAsAdded(false);
mHistPref = new BatteryHistoryPreference(getActivity(), mStatsHelper.getStats(),
mStatsHelper.getBatteryBroadcast());
mHistPref.setOrder(-1);
mAppListGroup.addPreference(mHistPref);
boolean addedSome = false;
final PowerProfile powerProfile = mStatsHelper.getPowerProfile();//每个器件的平均耗电
final BatteryStats stats = mStatsHelper.getStats();
final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP) {//如果没有配置原生的power_profile.xml,这里的averagePower 为0.1
Log.e("TAG", "enter averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP");
final List<UserHandle> profiles = mUm.getUserProfiles();
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles);
final List<BatterySipper> usageList = mStatsHelper.getUsageList();//获取各个耗电
final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0;//BatteryStatsService统计各个的时间会在usb线拔掉后清零,dichargeaAmount相当于usb拔掉后这段时间的电量,相当于电池使用的量
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
if ((sipper.value * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP) {//BatterySipper 的power都是以每小时计的
continue;
}
final double percentOfTotal =
((sipper.value / mStatsHelper.getTotalPower()) * dischargeAmount);//和上面一样(sipper.value / mStatsHelper.getTotalPower()统计的值,代表是该统计占该段时间内总的消耗电量的一个比,而该段时间是就是BatteryStats统计的时间,因此这段时间统计的电量还要乘以一个,这段时间内消耗的电量比上总电量的一个值。而且这个消耗电量的统计在BatteryStatsImpl中,当充电时,会把该项消耗电量统计下。
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {
// Don't show over-counted unless it is at least 2/3 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < ((mStatsHelper.getMaxRealPower()*2)/3)) {
continue;
}
if (percentOfTotal < 10) {
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {
// Don't show over-counted unless it is at least 1/2 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < (mStatsHelper.getMaxRealPower()/2)) {
continue;
}
if (percentOfTotal < 5) {
Log.e("kangkang", "7");
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(),
userHandle);
final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(),
userHandle);
final PowerGaugePreference pref = new PowerGaugePreference(getActivity(),
badgedIcon, contentDescription, entry);
final double percentOfMax = (sipper.value * 100) / mStatsHelper.getMaxPower();
sipper.percent = percentOfTotal;
pref.setTitle(entry.getLabel());
pref.setOrder(i + 1);
pref.setPercent(percentOfMax, percentOfTotal);
if (sipper.uidObj != null) {
pref.setKey(Integer.toString(sipper.uidObj.getUid()));
}
addedSome = true;
mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST + 1)) {
break;
}
}
}
if (!addedSome) {//如果走到这里会显示"没有电池数据",要么power_profile.xml没有配置,要么每项都continue
addNotAvailableMessage();
}
BatteryEntry.startRequestQueue();
}
碰到一个问题当电池电量到90%,拔去usb线,settings会显示“没有电池使用数据”,带着这个问题,我们再来看看BatteryStatsImpl:
我们先来看看BatteryStatsHelper中refreshStats函数中的一段代码,如何统计mTotalPower,先会计算一个mComputedPower,会把每个usage的power累计相加,然后下面还有一段代码,mStats.getLowDischargeAmountSinceCharge是什么意思呢?带着这个问题再去看BatteryStatsImpl的代码。
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
long rawUptimeUs) {
。。。。。。。。。。。。
mTotalPower = mComputedPower;
if (mStats.getLowDischargeAmountSinceCharge() > 1) {//
if (mMinDrainedPower > mComputedPower) {
double amount = mMinDrainedPower - mComputedPower;
mTotalPower = mMinDrainedPower;
addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);
} else if (mMaxDrainedPower < mComputedPower) {
double amount = mComputedPower - mMaxDrainedPower;
addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
}
}
Collections.sort(mUsageList);
}
先看BatteryStatsImpl中getLowDischargeAmountSinceCharge函数,直接从代码中找哪里改变了这个变量mLowDischargeAmountSinceCharge
@Override
public int getLowDischargeAmountSinceCharge() {
synchronized(this) {
int val = mLowDischargeAmountSinceCharge;
if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) {
val += mDischargeUnplugLevel-mDischargeCurrentLevel-1;
}
return val;
}
}
发现在setOnBatteryLocked函数中会改变其值,而setOnBatteryLocked函数是有BatteryService调用,只有当充电方式改变才会掉,而我们这段代码代表,只有插上usb线会调用,level代表当前电量,mDischargeUnplugLevel代表上一次拔去usb线的电量,这就代表上次拔去usb线后,电量level减少了,就是耗电了,在这次充电的时候把之前的耗电统计下,放在mLowDischargeAmountSinceCharge 和mHighDischargeAmountSinceCharge 这两个变量中,这两个变量差1,就是有一个误差的范围而已。
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
final int oldStatus, final int level) {
else {
mOnBattery = mOnBatteryInternal = onBattery;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargePlugLevel = level;
if (level < mDischargeUnplugLevel) {//level代表当前电量,mDischargeUnplugLevel代表上一次拔去usb线的电量
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;//累计消耗的电量
mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
}
updateDischargeScreenLevelsLocked(screenOn, screenOn);
updateTimeBasesLocked(false, !screenOn, uptime, realtime);
mNumChargeStepDurations = 0;
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
mLastChargeStepTime = -1;
mInitStepMode = mCurStepMode;
mModStepMode = 0;
}
现在我们再来看看getLowDischargeAmountSinceCharge函数。
@Override
public int getLowDischargeAmountSinceCharge() {
synchronized(this) {
int val = mLowDischargeAmountSinceCharge;
if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) {//表示现在正在用电状态,mDischargeCurrentLevel 变量代表用电的时候的电量,会时时更新,mDischargeUnplugLevel代表上一次拔去usb线的一个电量,也就是最近的一次消耗电量
val += mDischargeUnplugLevel-mDischargeCurrentLevel-1;
}
return val;
}
}
最近一次的消耗电量加上之前累计的消耗电量就是总的消耗电量了。
下面看看几个关键变量
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
final int oldStatus, final int level) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
mHandler.sendMessage(m);
final long uptime = mSecUptime * 1000;
final long realtime = mSecRealtime * 1000;
final boolean screenOn = mScreenState == Display.STATE_ON;
if (onBattery) {
。。。。。。。。。。
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;//刚拔去usb线的时候统计下mDischargeCurrentLevel和mDischargeUnplugLevel值
if (screenOn) {
mDischargeScreenOnUnplugLevel = level;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = level;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
updateTimeBasesLocked(true, !screenOn, uptime, realtime);
} else {
mOnBattery = mOnBatteryInternal = onBattery;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargePlugLevel = level;//刚插上usb线的时候统计下mDischargeCurrentLevel和mDischargePlugLevel值
setBatteryState是BatteryService时时调的,而setOnBatteryLocked只有充电方式改变后才会调用。
public void setBatteryState(int status, int health, int plugType, int level,
int temp, int volt) {
synchronized(this) {
final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
final long uptime = SystemClock.uptimeMillis();
final long elapsedRealtime = SystemClock.elapsedRealtime();
int oldStatus = mHistoryCur.batteryStatus;
if (!mHaveBatteryLevel) {
mHaveBatteryLevel = true;
// We start out assuming that the device is plugged in (not
// on battery). If our first report is now that we are indeed
// plugged in, then twiddle our state to correctly reflect that
// since we won't be going through the full setOnBattery().
if (onBattery == mOnBattery) {
if (onBattery) {
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
} else {
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
}
}
oldStatus = status;
}
if (onBattery) {
mDischargeCurrentLevel = level;//只要使用电池该mDischargeCurrentLevel 也是时时更新。
if (!mRecordingHistory) {
mRecordingHistory = true;
startRecordingHistory(elapsedRealtime, uptime, true);
}
} else if (level < 96) {
if (!mRecordingHistory) {
mRecordingHistory = true;
startRecordingHistory(elapsedRealtime, uptime, true);
}
}
再回到前面一开始的BatteryStatsHelper中的mTotalPower问题,
public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
long rawUptimeUs) {
。。。。。。。。。。。。
mTotalPower = mComputedPower;
if (mStats.getLowDischargeAmountSinceCharge() > 1) {//只要统计消耗的电量大于1
if (mMinDrainedPower > mComputedPower) {//如果实际总的消耗电量比统计的电量大,那么mTotalPower 就用实际消耗的电量
double amount = mMinDrainedPower - mComputedPower;
mTotalPower = mMinDrainedPower;
addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);//并且将没有统计到的电量,也放在usgaelist中
} else if (mMaxDrainedPower < mComputedPower) {
double amount = mComputedPower - mMaxDrainedPower;
addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
}
}
Collections.sort(mUsageList);
}
再看看mMinDrainedPower 和mMaxDrainedPower 两个变量,就是消耗电量除以100,乘以总电量就是消耗的实际电量而不是百分比了。
mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
* mPowerProfile.getBatteryCapacity()) / 100;
mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
* mPowerProfile.getBatteryCapacity()) / 100;
回过头来看看settings为什么90%之后,拔掉usb线就没有电池数据了,settings会遍历所有的usagelist,只有所有的都continue才会出现没有电池数据,一个可能及时dischargeAmount为0,mStatsHelper.getTotalPower,和sipper.value
mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles);
final List<BatterySipper> usageList = mStatsHelper.getUsageList();
final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0;
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
if ((sipper.value * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP) {
continue;
}
final double percentOfTotal =
((sipper.value / mStatsHelper.getTotalPower()) * dischargeAmount);
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {
// Don't show over-counted unless it is at least 2/3 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < ((mStatsHelper.getMaxRealPower()*2)/3)) {
continue;
}
if (percentOfTotal < 10) {
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {
// Don't show over-counted unless it is at least 1/2 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < (mStatsHelper.getMaxRealPower()/2)) {
continue;
}
if (percentOfTotal < 5) {
continue;
}
if ("user".equals(Build.TYPE)) {
continue;
}
}
而我们看看BatteryStatsImpl中getDischargeAmount就是getHighDischargeAmountSinceCharge函数,而getHighDischargeAmountSinceCharge就是统计了电池消耗的电量,只是范围比较大的那个值,所有到90%没有关系。
@Override
public int getDischargeAmount(int which) {
int dischargeAmount = which == STATS_SINCE_CHARGED
? getHighDischargeAmountSinceCharge()
: (getDischargeStartLevel() - getDischargeCurrentLevel());
if (dischargeAmount < 0) {
dischargeAmount = 0;
}
return dischargeAmount;
}
mStatsHelper.getTotalPower也是BatteryStatsHelper统计的消耗电量,和到90%也没有关系。
那么剩下只有一个可能,sipper.value 为0. 而每个usage的消耗电量为0,只有是统计的时间为0.
再去BatteryStatsImpl看看什么时候会将统计的时间置为0.
在resetAllStatsLocked接口里会把所有的timer重新reset,之后看看谁调用了这个函数
private void resetAllStatsLocked() {
mStartCount = 0;
initTimes(SystemClock.uptimeMillis() * 1000, SystemClock.elapsedRealtime() * 1000);
mScreenOnTimer.reset(false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false);
}
mInteractiveTimer.reset(false);
mLowPowerModeEnabledTimer.reset(false);
mPhoneOnTimer.reset(false);
mAudioOnTimer.reset(false);
mVideoOnTimer.reset(false);
mFlashlightOnTimer.reset(false);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].reset(false);
}
mPhoneSignalScanningTimer.reset(false);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].reset(false);
}
结果就发现在setOnBatteryLocked
void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
final int oldStatus, final int level) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
mHandler.sendMessage(m);
final long uptime = mSecUptime * 1000;
final long realtime = mSecRealtime * 1000;
final boolean screenOn = mScreenState == Display.STATE_ON;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
// we have gone through a significant charge (from a very low
// level to a now very high level).
boolean reset = false;
if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|| level >= 90//当当前电量大于90%
|| (mDischargeCurrentLevel < 20 && level >= 80)//当上次插电时的电量是20,现在充电充到80
|| (getHighDischargeAmountSinceCharge() >= 200
&& mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER))) {
Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
+ " dischargeLevel=" + mDischargeCurrentLevel
+ " lowAmount=" + getLowDischargeAmountSinceCharge()
+ " highAmount=" + getHighDischargeAmountSinceCharge());
// Before we write, collect a snapshot of the final aggregated
// stats to be reported in the next checkin. Only do this if we have
// a sufficient amount of data to make it interesting.
if (getLowDischargeAmountSinceCharge() >= 20) {
final Parcel parcel = Parcel.obtain();
writeSummaryToParcel(parcel, true);
BackgroundThread.getHandler().post(new Runnable() {
@Override public void run() {
synchronized (mCheckinFile) {
FileOutputStream stream = null;
try {
stream = mCheckinFile.startWrite();
stream.write(parcel.marshall());
stream.flush();
FileUtils.sync(stream);
stream.close();
mCheckinFile.finishWrite(stream);
} catch (IOException e) {
Slog.w("BatteryStats",
"Error writing checkin battery statistics", e);
mCheckinFile.failWrite(stream);
} finally {
parcel.recycle();
}
}
}
});
}
doWrite = true;
resetAllStatsLocked();//会将所有的timer,重置
mDischargeStartLevel = level;
reset = true;
mNumDischargeStepDurations = 0;
}
好了,现在有答案了,当电量大于90的时候,BatteryStatsImpl会将所有timer重置,导致BatteryStatsHelper的各个Usage统计的电量为0,之后Settings就会在遍历每个usage的时候continue,然后就会显示没有电池使用数据了。