Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f094b876 authored by jackqdyulei's avatar jackqdyulei Committed by Lei Yu
Browse files

Add summary for categories in battery advanced page

If the category only contains one app, show usage time, otherwise
show app with maximum usage.

Also add usage time for apps in battery settings page.

Bug: 35396770
Test: RunSettingsRoboTests
Change-Id: I43fe9c2289535be2c1b95ffded6b52b0ff099589
(cherry picked from commit 3bbaca9c)
parent a48cb8d9
Loading
Loading
Loading
Loading
+100 −2
Original line number Diff line number Diff line
@@ -13,22 +13,29 @@
 */
package com.android.settings.fuelgauge;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;

import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceController;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;
import com.android.settings.overlay.FeatureFactory;
@@ -64,6 +71,38 @@ public class PowerUsageAdvanced extends PowerUsageBase {
    private PreferenceGroup mUsageListGroup;
    private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
    private PackageManager mPackageManager;
    private UserManager mUserManager;
    private Map<Integer, PowerUsageData> mBatteryDataMap;

    Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BatteryEntry.MSG_UPDATE_NAME_ICON:
                    final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(
                            STATUS_TYPE);
                    final double totalPower = mStatsHelper.getTotalPower();
                    final BatteryEntry entry = (BatteryEntry) msg.obj;
                    final int usageType = extractUsageType(entry.sipper);

                    PowerUsageData usageData = mBatteryDataMap.get(usageType);
                    Preference pref = findPreference(String.valueOf(usageType));
                    if (pref != null && usageData != null) {
                        updateUsageDataSummary(usageData, totalPower, dischargeAmount);
                        pref.setSummary(usageData.summary);
                    }
                    break;
                case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
                    Activity activity = getActivity();
                    if (activity != null) {
                        activity.reportFullyDrawn();
                    }
                    break;
            }
            super.handleMessage(msg);
        }
    };

    @Override
    public void onCreate(Bundle icicle) {
@@ -76,6 +115,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
        mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
                .getPowerUsageFeatureProvider(context);
        mPackageManager = context.getPackageManager();
        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
    }

    @Override
@@ -84,6 +124,21 @@ public class PowerUsageAdvanced extends PowerUsageBase {
        refreshStats();
    }

    @Override
    public void onPause() {
        BatteryEntry.stopRequestQueue();
        mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
        super.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (getActivity().isChangingConfigurations()) {
            BatteryEntry.clearUidCache();
        }
    }

    @Override
    public int getMetricsCategory() {
        return MetricsProto.MetricsEvent.FUELGAUGE_BATTERY_HISTORY_DETAIL;
@@ -116,15 +171,19 @@ public class PowerUsageAdvanced extends PowerUsageBase {
            final PowerUsageData batteryData = dataList.get(i);
            final PowerGaugePreference pref = new PowerGaugePreference(getPrefContext());

            pref.setKey(String.valueOf(batteryData.usageType));
            pref.setTitle(batteryData.titleResId);
            pref.setSummary(batteryData.summary);
            pref.setPercent(batteryData.percentage);
            mUsageListGroup.addPreference(pref);
        }

        BatteryEntry.startRequestQueue();
    }

    @VisibleForTesting
    @UsageType int extractUsageType(BatterySipper sipper) {
    @UsageType
    int extractUsageType(BatterySipper sipper) {
        final DrainType drainType = sipper.drainType;
        final int uid = sipper.getUid();

@@ -163,21 +222,56 @@ public class PowerUsageAdvanced extends PowerUsageBase {
            sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
            final PowerUsageData usageData = batteryDataMap.get(extractUsageType(sipper));
            usageData.totalPowerMah += sipper.totalPowerMah;
            if (sipper.drainType == DrainType.APP && sipper.usageTimeMs != 0) {
                sipper.usageTimeMs = BatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.ALL,
                        sipper.uidObj, STATUS_TYPE);
            }
            usageData.totalUsageTimeMs += sipper.usageTimeMs;
            usageData.usageList.add(sipper);
        }

        // TODO(b/35396770): add logic to extract the summary
        final List<PowerUsageData> batteryDataList = new ArrayList<>(batteryDataMap.values());
        final int dischargeAmount = statusHelper.getStats().getDischargeAmount(STATUS_TYPE);
        final double totalPower = statusHelper.getTotalPower();
        for (final PowerUsageData usageData : batteryDataList) {
            usageData.percentage = (usageData.totalPowerMah / totalPower) * dischargeAmount;
            updateUsageDataSummary(usageData, totalPower, dischargeAmount);
        }

        Collections.sort(batteryDataList);

        mBatteryDataMap = batteryDataMap;
        return batteryDataList;
    }

    @VisibleForTesting
    void updateUsageDataSummary(PowerUsageData usageData, double totalPower, int dischargeAmount) {
        if (usageData.usageList.size() <= 1) {
            usageData.summary = getString(R.string.battery_used_for,
                    Utils.formatElapsedTime(getContext(), usageData.totalUsageTimeMs, false));
        } else {
            BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
            BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
                    sipper);
            final double percentage = (sipper.totalPowerMah / totalPower) * dischargeAmount;
            usageData.summary = getString(R.string.battery_used_by,
                    Utils.formatPercentage(percentage, true), batteryEntry.name);
        }
    }

    @VisibleForTesting
    BatterySipper findBatterySipperWithMaxBatteryUsage(List<BatterySipper> usageList) {
        BatterySipper sipper = usageList.get(0);
        for (int i = 1, size = usageList.size(); i < size; i++) {
            final BatterySipper comparedSipper = usageList.get(i);
            if (comparedSipper.totalPowerMah > sipper.totalPowerMah) {
                sipper = comparedSipper;
            }
        }

        return sipper;
    }

    @VisibleForTesting
    void setPackageManager(PackageManager packageManager) {
        mPackageManager = packageManager;
@@ -221,10 +315,12 @@ public class PowerUsageAdvanced extends PowerUsageBase {
        public String summary;
        public double percentage;
        public double totalPowerMah;
        public long totalUsageTimeMs;
        @ColorInt
        public int iconColor;
        @UsageType
        public int usageType;
        public List<BatterySipper> usageList;

        public PowerUsageData(@UsageType int usageType) {
            this(usageType, 0);
@@ -233,8 +329,10 @@ public class PowerUsageAdvanced extends PowerUsageBase {
        public PowerUsageData(@UsageType int usageType, double totalPower) {
            this.usageType = usageType;
            totalPowerMah = 0;
            totalUsageTimeMs = 0;
            titleResId = getTitleResId(usageType);
            totalPowerMah = totalPower;
            usageList = new ArrayList<>();
        }

        private int getTitleResId(@UsageType int usageType) {
+4 −0
Original line number Diff line number Diff line
@@ -489,6 +489,10 @@ public class PowerUsageSummary extends PowerUsageBase {
                pref.setTitle(entry.getLabel());
                pref.setOrder(i + 1);
                pref.setPercent(percentOfTotal);
                if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
                    sipper.usageTimeMs = BatteryUtils.getProcessTimeMs(
                            BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
                }
                setUsageSummary(pref, usedTime, sipper.usageTimeMs);
                if ((sipper.drainType != DrainType.APP
                        || sipper.uidObj.getUid() == Process.ROOT_UID)
+66 −11
Original line number Diff line number Diff line
@@ -15,31 +15,40 @@
 */
package com.android.settings.fuelgauge;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;

import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(SettingsRobolectricTestRunner.class)
@@ -53,9 +62,13 @@ public class PowerUsageAdvancedTest {
    private static final double TYPE_WIFI_USAGE = 0;
    private static final double TOTAL_USAGE = TYPE_APP_USAGE * 2 + TYPE_BLUETOOTH_USAGE
            + TYPE_WIFI_USAGE;
    private static final double TOTAL_POWER = 500;
    private static final double PRECISION = 0.001;
    private static final String STUB_STRING = "stub_string";
    @Mock
    private BatterySipper mNormalBatterySipper;
    @Mock
    private BatterySipper mBatterySipper;
    private BatterySipper mMaxBatterySipper;
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private BatteryStatsHelper mBatteryStatsHelper;
    @Mock
@@ -63,11 +76,14 @@ public class PowerUsageAdvancedTest {
    @Mock
    private PackageManager mPackageManager;
    private PowerUsageAdvanced mPowerUsageAdvanced;
    private PowerUsageData mPowerUsageData;
    private Context mShadowContext;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mPowerUsageAdvanced = new PowerUsageAdvanced();
        mShadowContext = RuntimeEnvironment.application;
        mPowerUsageAdvanced = spy(new PowerUsageAdvanced());

        List<BatterySipper> batterySippers = new ArrayList<>();
        batterySippers.add(new BatterySipper(DrainType.APP,
@@ -83,16 +99,24 @@ public class PowerUsageAdvancedTest {
                DISCHARGE_AMOUNT);
        when(mBatteryStatsHelper.getUsageList()).thenReturn(batterySippers);
        when(mBatteryStatsHelper.getTotalPower()).thenReturn(TOTAL_USAGE);
        when(mPowerUsageAdvanced.getContext()).thenReturn(mShadowContext);
        doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any(), any());
        doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any());
        mPowerUsageAdvanced.setPackageManager(mPackageManager);
        mPowerUsageAdvanced.setPowerUsageFeatureProvider(mPowerUsageFeatureProvider);

        mPowerUsageData = new PowerUsageData(UsageType.APP);
        mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
        mMaxBatterySipper.drainType = DrainType.BLUETOOTH;
        mNormalBatterySipper.drainType = DrainType.SCREEN;
    }

    @Test
    public void testExtractUsageType_TypeSystem_ReturnSystem() {
        mBatterySipper.drainType = DrainType.APP;
        mNormalBatterySipper.drainType = DrainType.APP;
        when(mPowerUsageFeatureProvider.isTypeSystem(any())).thenReturn(true);

        assertThat(mPowerUsageAdvanced.extractUsageType(mBatterySipper))
        assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
                .isEqualTo(UsageType.SYSTEM);
    }

@@ -105,19 +129,19 @@ public class PowerUsageAdvancedTest {

        assertThat(drainTypes.length).isEqualTo(usageTypes.length);
        for (int i = 0, size = drainTypes.length; i < size; i++) {
            mBatterySipper.drainType = drainTypes[i];
            assertThat(mPowerUsageAdvanced.extractUsageType(mBatterySipper))
            mNormalBatterySipper.drainType = drainTypes[i];
            assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
                    .isEqualTo(usageTypes[i]);
        }
    }

    @Test
    public void testExtractUsageType_TypeService_ReturnService() {
        mBatterySipper.drainType = DrainType.APP;
        when(mBatterySipper.getUid()).thenReturn(FAKE_UID_1);
        mNormalBatterySipper.drainType = DrainType.APP;
        when(mNormalBatterySipper.getUid()).thenReturn(FAKE_UID_1);
        when(mPowerUsageFeatureProvider.isTypeService(any())).thenReturn(true);

        assertThat(mPowerUsageAdvanced.extractUsageType(mBatterySipper))
        assertThat(mPowerUsageAdvanced.extractUsageType(mNormalBatterySipper))
                .isEqualTo(UsageType.SERVICE);
    }

@@ -146,6 +170,37 @@ public class PowerUsageAdvancedTest {
        }
    }

    @Test
    public void testUpdateUsageDataSummary_onlyOneApp_showUsageTime() {
        mPowerUsageData.usageList.add(mNormalBatterySipper);
        mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);

        verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_for), any());
    }

    @Test
    public void testUpdateUsageDataSummary_moreThanOneApp_showMaxUsageApp() {
        mPowerUsageData.usageList.add(mNormalBatterySipper);
        mPowerUsageData.usageList.add(mMaxBatterySipper);
        doReturn(mMaxBatterySipper).when(mPowerUsageAdvanced).findBatterySipperWithMaxBatteryUsage(
                mPowerUsageData.usageList);
        final double percentage = (TYPE_BLUETOOTH_USAGE / TOTAL_POWER) * DISCHARGE_AMOUNT;
        mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);

        verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_by),
                eq(Utils.formatPercentage(percentage, true)), any());
    }

    @Test
    public void testFindBatterySipperWithMaxBatteryUsage_findCorrectOne() {
        mPowerUsageData.usageList.add(mNormalBatterySipper);
        mPowerUsageData.usageList.add(mMaxBatterySipper);
        BatterySipper sipper = mPowerUsageAdvanced.findBatterySipperWithMaxBatteryUsage(
                mPowerUsageData.usageList);

        assertThat(sipper).isEqualTo(mMaxBatterySipper);
    }

    @Test
    public void testInit_ContainsAllUsageType() {
        final int[] usageTypeSet = mPowerUsageAdvanced.mUsageTypes;