1.概要
Android中Settings->Battery->Battery usage中,可以查看电池电量使用情况。(充满电的状态下,各个应用消耗电量的实际情况)。
电池电量实际使用情况,其实是根据power_profile.xml中的配置有关,包括Screen亮灭屏、Wifi、Bluetooth等,通过计算app在一段时间内使用的相关资源的电量,估算出耗电量,并显示出来。
2.问题表现
进入Battery usage后,通过放电应用放电一定时间后,显示"没有电池使用数据"。
2.1.调查过程
通过Monitor获取当前布局,得到BatteryAppListPreferenceController.java(项目不同可能java类命名不同)。
在当前类中主要代码:
public boolean shouldShowBatteryAttributionList(Context context) {
......
PowerProfile powerProfile = new PowerProfile(context);
// Cheap hack to try to figure out if the power_profile.xml was populated.
final double averagePowerForOrdinal = powerProfile.getAveragePowerForOrdinal(
PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0);
final boolean shouldShowBatteryAttributionList =
averagePowerForOrdinal >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP;
if (!shouldShowBatteryAttributionList) {
Log.w(TAG, "shouldShowBatteryAttributionList(): " + averagePowerForOrdinal);
}
return shouldShowBatteryAttributionList;
}
shouldShowBatteryAttributionList是判断电池使用数据是否显示的boolean值,实际上跟averagePowerForOrdinal >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP 有关。
MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
averagePowerForOrdinal 则通过 POWER_GROUP_DISPLAY_SCREEN_FULL 得到。
因此,在PowerProfile.java中去找POWER_GROUP_DISPLAY_SCREEN_FULL的实际值:
![]()
在对应power_profile.xml的参数:
<!-- Display related values. -->
<!-- Average battery current draw of display0 while in ambient mode, including backlight.
There must be one of these for each display, labeled:
ambient.on.display0, ambient.on.display1, etc...
Each display suffix number should match it's ordinal in its display device config.
-->
<item name="ambient.on.display0">230.4</item> <!-- ~100mA -->
<!-- Average battery current draw of display0 while on without backlight.
There must be one of these for each display, labeled:
screen.on.display0, screen.on.display1, etc...
Each display suffix number should match it's ordinal in its display device config.
-->
<item name="screen.on.display0">196.4</item> <!-- ~100mA -->
<!-- Average battery current draw of the backlight at full brightness.
The full current draw of display N at full brightness should be the sum of screen.on.displayN
and screen.full.displayN
There must be one of these for each display, labeled:
screen.full.display0, screen.full.display1, etc...
Each display suffix number should match it's ordinal in its display device config.
-->
<item name="screen.full.display0">307.1</item> <!-- ~100mA -->
<item name="bluetooth.active">157.7</item> <!-- Bluetooth data transfer, ~10mA -->
这里的screen.full.display0就是对应的POWER_GROUP_DISPLAY_SCREEN_FULL的值。如果设置的值小于10,则不会显示应用的耗电量:
public void refreshAppListGroup(BatteryUsageStats batteryUsageStats, boolean showAllApps) {
if (!isAvailable()) {
return;
}
mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats;
mAppListGroup.setTitle(R.string.power_usage_list_summary);
// 在后面会用到这个值,当前是false
boolean addedSome = false;
cacheRemoveAllPrefs(mAppListGroup);
mAppListGroup.setOrderingAsAdded(false);
// 如果之前的shouldShowBatteryAttributionList 为true,则进入此方法,正常计算并显示耗电量
if (sConfig.shouldShowBatteryAttributionList(mContext)) {
final int dischargePercentage = getDischargePercentage(batteryUsageStats);
final List<BatteryEntry> usageList =
getCoalescedUsageList(showAllApps, /*loadDataInBackground=*/ true);
final double totalPower = batteryUsageStats.getConsumedPower();
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatteryEntry entry = usageList.get(i);
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
entry.getConsumedPower(), totalPower, dischargePercentage);
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
final int uid = entry.getUid();
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(uid));
final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(),
userHandle);
final CharSequence contentDescription = mUserManager.getBadgedLabelForUser(
entry.getLabel(), userHandle);
final String key = entry.getKey();
PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
if (pref == null) {
pref = new PowerGaugePreference(mPrefContext, badgedIcon,
contentDescription, entry);
pref.setKey(key);
}
entry.mPercent = percentOfTotal;
pref.setTitle(entry.getLabel());
pref.setOrder(i + 1);
pref.setPercent(percentOfTotal);
pref.shouldShowAnomalyIcon(false);
pref.setEnabled(uid != BatteryUtils.UID_TETHERING
&& uid != BatteryUtils.UID_REMOVED_APPS);
setUsageSummary(pref, entry);
addedSome = true;
mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() - getCachedCount()
> (MAX_ITEMS_TO_LIST + 1)) {
break;
}
}
}
// 如果shouldShowBatteryAttributionList 为true,在上面的代码中,会将addedSome修改为 true,否则仍为false
// “没有电池使用数据”显示的时候,shouldShowBatteryAttributionList为false,因此进入了如下的判断,调用了addNotAvailableMessage()方法。
if (!addedSome) {
addNotAvailableMessage();
}
removeCachedPrefs(mAppListGroup);
BatteryEntry.startRequestQueue();
}
"没有电池使用数据"显示的时候,shouldShowBatteryAttributionList为false,因此进入了如下的判断,调用了addNotAvailableMessage()方法。我们来看看这个方法中主要做了什么:
private void addNotAvailableMessage() {
Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
if (notAvailable == null) {
notAvailable = new Preference(mPrefContext);
notAvailable.setKey(NOT_AVAILABLE);
// 就是这里 显示了 “没有电池使用数据”
notAvailable.setTitle(R.string.power_usage_not_available);
notAvailable.setSelectable(false);
mAppListGroup.addPreference(notAvailable);
}
}
3.结论
因此,电池使用数据与frameworks/base/core/res/res/xml/power_profile.xml中的screen.full.display有着紧密关系。此外,power_profile.xml中的数据需要根据硬件测试提供的实际数据来进行调整,如果有对应的overlay目录,配置在对应的overlay目录下更好。
注:此为自己所在项目调查时的结论,与实际可能有偏差。不足之处请大佬们指出。