Android Battery电池电量使用详情

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目录下更好。

注:此为自己所在项目调查时的结论,与实际可能有偏差。不足之处请大佬们指出。