Merge "Move bunch of battery methods to BatteryUtils.java" into oc-dev

This commit is contained in:
Lei Yu
2017-04-12 19:39:08 +00:00
committed by Android (Google) Code Review
5 changed files with 228 additions and 145 deletions

View File

@@ -91,6 +91,7 @@ import com.android.settings.datausage.DataUsageList;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
import com.android.settings.fuelgauge.BatteryEntry;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.NotificationBackend.AppRow;
@@ -197,6 +198,7 @@ public class InstalledAppDetails extends AppInfoBase
private AppStorageStats mLastResult;
private String mBatteryPercent;
private BatteryUtils mBatteryUtils;
private boolean handleDisableable(Button button) {
boolean disableable = false;
@@ -357,6 +359,7 @@ public class InstalledAppDetails extends AppInfoBase
removePreference(KEY_DATA);
}
mBatteryHelper = new BatteryStatsHelper(getActivity(), true);
mBatteryUtils = BatteryUtils.getInstance(getContext());
}
@Override
@@ -685,10 +688,14 @@ public class InstalledAppDetails extends AppInfoBase
private void updateBattery() {
if (mSipper != null) {
mBatteryPreference.setEnabled(true);
int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount(
final int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount(
BatteryStats.STATS_SINCE_CHARGED);
final int percentOfMax = (int) ((mSipper.totalPowerMah)
/ mBatteryHelper.getTotalPower() * dischargeAmount + .5f);
final List<BatterySipper> usageList = new ArrayList<>(mBatteryHelper.getUsageList());
final double hiddenAmount = mBatteryUtils.removeHiddenBatterySippers(usageList);
final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
mSipper.totalPowerMah, mBatteryHelper.getTotalPower(), hiddenAmount,
dischargeAmount);
mBatteryPercent = Utils.formatPercentage(percentOfMax);
mBatteryPreference.setSummary(getString(R.string.battery_summary, mBatteryPercent));
} else {

View File

@@ -22,10 +22,15 @@ import android.os.BatteryStats;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.Log;
import com.android.internal.os.BatterySipper;
import com.android.settings.overlay.FeatureFactory;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* Utils for battery operation
@@ -43,9 +48,14 @@ public class BatteryUtils {
}
private static final String TAG = "BatteryUtils";
private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5;
private static final int SECONDS_IN_HOUR = 60 * 60;
private static BatteryUtils sInstance;
private PackageManager mPackageManager;
@VisibleForTesting
PowerUsageFeatureProvider mPowerUsageFeatureProvider;
public static BatteryUtils getInstance(Context context) {
if (sInstance == null || sInstance.isDataCorrupted()) {
@@ -56,6 +66,8 @@ public class BatteryUtils {
private BatteryUtils(Context context) {
mPackageManager = context.getPackageManager();
mPowerUsageFeatureProvider = FeatureFactory.getFactory(
context).getPowerUsageFeatureProvider(context);
}
public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid,
@@ -105,6 +117,67 @@ public class BatteryUtils {
return convertUsToMs(timeUs);
}
/**
* Remove the {@link BatterySipper} that we should hide.
*
* @param sippers sipper list that need to check and remove
* @return the total power of the hidden items of {@link BatterySipper}
*/
public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
double totalPowerMah = 0;
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper sipper = sippers.get(i);
if (shouldHideSipper(sipper)) {
sippers.remove(i);
if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
&& sipper.drainType != BatterySipper.DrainType.UNACCOUNTED) {
// Don't add it if it is overcounted or unaccounted
totalPowerMah += sipper.totalPowerMah;
}
}
}
return totalPowerMah;
}
/**
* Check whether we should hide the battery sipper.
*/
public boolean shouldHideSipper(BatterySipper sipper) {
final BatterySipper.DrainType drainType = sipper.drainType;
return drainType == BatterySipper.DrainType.IDLE
|| drainType == BatterySipper.DrainType.CELL
|| drainType == BatterySipper.DrainType.WIFI
|| drainType == BatterySipper.DrainType.SCREEN
|| drainType == BatterySipper.DrainType.BLUETOOTH
|| drainType == BatterySipper.DrainType.UNACCOUNTED
|| drainType == BatterySipper.DrainType.OVERCOUNTED
|| (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP
|| mPowerUsageFeatureProvider.isTypeService(sipper)
|| mPowerUsageFeatureProvider.isTypeSystem(sipper);
}
/**
* Calculate the power usage percentage for an app
*
* @param powerUsageMah power used by the app
* @param totalPowerMah total power used in the system
* @param hiddenPowerMah power used by no-actionable app that we want to hide, i.e. Screen,
* Android OS.
* @param dischargeAmount The discharge amount calculated by {@link BatteryStats}
* @return A percentage value scaled by {@paramref dischargeAmount}
* @see BatteryStats#getDischargeAmount(int)
*/
public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah,
double hiddenPowerMah, int dischargeAmount) {
if (totalPowerMah == 0) {
return 0;
}
return (powerUsageMah / (totalPowerMah - hiddenPowerMah)) * dischargeAmount;
}
private long convertUsToMs(long timeUs) {
return timeUs / 1000;
}

View File

@@ -80,11 +80,8 @@ public class PowerUsageSummary extends PowerUsageBase {
private static final boolean USE_FAKE_DATA = false;
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header";
private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5;
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final int SECONDS_IN_HOUR = 60 * 60;
private static final String KEY_SCREEN_USAGE = "screen_usage";
private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge";
@@ -432,20 +429,16 @@ public class PowerUsageSummary extends PowerUsageBase {
final List<BatterySipper> usageList = getCoalescedUsageList(
USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());
double hiddenPowerMah = mShowAllApps ? 0 : removeHiddenBatterySippers(usageList);
double hiddenPowerMah = mShowAllApps ? 0 :
mBatteryUtils.removeHiddenBatterySippers(usageList);
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
// Deduct the power of hidden items from total power, which is used to
// calculate percentOfTotal
double totalPower = USE_FAKE_DATA ?
4000 : mStatsHelper.getTotalPower() - hiddenPowerMah;
double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower();
// With deduction in totalPower, percentOfTotal is higher because it adds the part
// used in screen, system, etc
final double percentOfTotal = totalPower == 0 ? 0 :
((sipper.totalPowerMah / totalPower) * dischargeAmount);
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
if (((int) (percentOfTotal + .5)) < 1) {
continue;
@@ -594,22 +587,6 @@ public class PowerUsageSummary extends PowerUsageBase {
}
}
@VisibleForTesting
boolean shouldHideSipper(BatterySipper sipper) {
final DrainType drainType = sipper.drainType;
return drainType == DrainType.IDLE
|| drainType == DrainType.CELL
|| drainType == DrainType.WIFI
|| drainType == DrainType.SCREEN
|| drainType == DrainType.BLUETOOTH
|| drainType == DrainType.UNACCOUNTED
|| drainType == DrainType.OVERCOUNTED
|| (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP
|| mPowerFeatureProvider.isTypeService(sipper)
|| mPowerFeatureProvider.isTypeSystem(sipper);
}
@VisibleForTesting
String extractKeyFromSipper(BatterySipper sipper) {
if (sipper.uidObj != null) {
@@ -624,24 +601,6 @@ public class PowerUsageSummary extends PowerUsageBase {
}
}
@VisibleForTesting
double removeHiddenBatterySippers(List<BatterySipper> sippers) {
double totalPowerMah = 0;
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper sipper = sippers.get(i);
if (shouldHideSipper(sipper)) {
sippers.remove(i);
if (sipper.drainType != DrainType.OVERCOUNTED
&& sipper.drainType != DrainType.UNACCOUNTED) {
// Don't add it if it is overcounted or unaccounted
totalPowerMah += sipper.totalPowerMah;
}
}
}
return totalPowerMah;
}
@VisibleForTesting
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
mBatteryLayoutPref = layoutPreference;

View File

@@ -15,14 +15,19 @@
*/
package com.android.settings.fuelgauge;
import android.content.Context;
import android.os.BatteryStats;
import android.os.Process;
import com.android.internal.os.BatterySipper;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.FakeFeatureFactory;
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;
@@ -36,10 +41,15 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -52,18 +62,45 @@ public class BatteryUtilsTest {
private static final long TIME_STATE_FOREGROUND = 3000 * UNIT;
private static final long TIME_STATE_BACKGROUND = 6000 * UNIT;
private static final int UID = 123;
private static final long TIME_EXPECTED_FOREGROUND = 9000;
private static final long TIME_EXPECTED_BACKGROUND = 6000;
private static final long TIME_EXPECTED_ALL = 15000;
private static final double BATTERY_SCREEN_USAGE = 300;
private static final double BATTERY_SYSTEM_USAGE = 600;
private static final double BATTERY_OVERACCOUNTED_USAGE = 500;
private static final double TOTAL_BATTERY_USAGE = 1000;
private static final double HIDDEN_USAGE = 200;
private static final int DISCHARGE_AMOUNT = 80;
private static final double PERCENT_SYSTEM_USAGE = 60;
private static final double PRECISION = 0.001;
@Mock
private BatteryStats.Uid mUid;
@Mock
private BatterySipper mNormalBatterySipper;
@Mock
private BatterySipper mScreenBatterySipper;
@Mock
private BatterySipper mOvercountedBatterySipper;
@Mock
private BatterySipper mSystemBatterySipper;
@Mock
private BatterySipper mCellBatterySipper;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private BatteryUtils mBatteryUtils;
private FakeFeatureFactory mFeatureFactory;
private PowerUsageFeatureProvider mProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest(mContext);
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mProvider = mFeatureFactory.powerUsageFeatureProvider;
doReturn(TIME_STATE_TOP).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(),
anyInt());
doReturn(TIME_STATE_FOREGROUND_SERVICE).when(mUid).getProcessStateTime(
@@ -75,7 +112,21 @@ public class BatteryUtilsTest {
doReturn(TIME_STATE_BACKGROUND).when(mUid).getProcessStateTime(eq(PROCESS_STATE_BACKGROUND),
anyLong(), anyInt());
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE;
mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;
mSystemBatterySipper.drainType = BatterySipper.DrainType.APP;
mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE;
when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);
mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERACCOUNTED_USAGE;
mBatteryUtils = BatteryUtils.getInstance(RuntimeEnvironment.application);
mBatteryUtils.mPowerUsageFeatureProvider = mProvider;
}
@Test
@@ -109,4 +160,92 @@ public class BatteryUtilsTest {
assertThat(time).isEqualTo(0);
}
@Test
public void testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue() {
final List<BatterySipper> sippers = new ArrayList<>();
sippers.add(mNormalBatterySipper);
sippers.add(mScreenBatterySipper);
sippers.add(mSystemBatterySipper);
sippers.add(mOvercountedBatterySipper);
when(mProvider.isTypeSystem(mSystemBatterySipper))
.thenReturn(true);
final double totalUsage = mBatteryUtils.removeHiddenBatterySippers(sippers);
assertThat(sippers).containsExactly(mNormalBatterySipper);
assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SCREEN_USAGE + BATTERY_SYSTEM_USAGE);
}
@Test
public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeIdle_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE;
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeWifi_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.WIFI;
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeCell_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL;
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeScreen_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeBluetooth_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeSystem_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID);
when(mProvider.isTypeSystem(any())).thenReturn(true);
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_UidNormal_ReturnFalse() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(UID);
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isFalse();
}
@Test
public void testShouldHideSipper_TypeService_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(UID);
when(mProvider.isTypeService(any())).thenReturn(true);
assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testCalculateBatteryPercent() {
assertThat(mBatteryUtils.calculateBatteryPercent(BATTERY_SYSTEM_USAGE, TOTAL_BATTERY_USAGE,
HIDDEN_USAGE, DISCHARGE_AMOUNT))
.isWithin(PRECISION).of(PERCENT_SYSTEM_USAGE);
}
}

View File

@@ -113,10 +113,6 @@ public class PowerUsageSummaryTest {
@Mock
private BatterySipper mScreenBatterySipper;
@Mock
private BatterySipper mOvercountedBatterySipper;
@Mock
private BatterySipper mSystemBatterySipper;
@Mock
private BatterySipper mCellBatterySipper;
@Mock
private PowerGaugePreference mPreference;
@@ -184,16 +180,8 @@ public class PowerUsageSummaryTest {
mFragment.setBatteryLayoutPreference(mBatteryLayoutPref);
mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;
mScreenBatterySipper.usageTimeMs = USAGE_TIME_MS;
mSystemBatterySipper.drainType = BatterySipper.DrainType.APP;
mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE;
when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);
mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERCOUNTED_USAGE;
mUsageList = new ArrayList<>();
mUsageList.add(mNormalBatterySipper);
mUsageList.add(mScreenBatterySipper);
@@ -304,90 +292,7 @@ public class PowerUsageSummaryTest {
}
@Test
public void testRemoveHiddenBatterySippers_containsHiddenSippers_removeAndReturnValue() {
final List<BatterySipper> sippers = new ArrayList<>();
sippers.add(mNormalBatterySipper);
sippers.add(mScreenBatterySipper);
sippers.add(mSystemBatterySipper);
sippers.add(mOvercountedBatterySipper);
when(mFeatureFactory.powerUsageFeatureProvider.isTypeSystem(mSystemBatterySipper))
.thenReturn(true);
final double totalUsage = mFragment.removeHiddenBatterySippers(sippers);
assertThat(sippers).containsExactly(mNormalBatterySipper);
assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SCREEN_USAGE + BATTERY_SYSTEM_USAGE);
}
@Test
public void testShouldHideSipper_typeIdle_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE;
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_TypeOverCounted_ReturnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_typeWifi_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.WIFI;
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_typeCell_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL;
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_typeScreen_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_typeBluetooth_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_typeSystem_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID);
when(mFeatureFactory.powerUsageFeatureProvider.isTypeSystem(Matchers.<BatterySipper>any()))
.thenReturn(true);
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_uidNormal_returnFalse() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(UID);
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isFalse();
}
@Test
public void testShouldHideSipper_typeService_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
when(mNormalBatterySipper.getUid()).thenReturn(UID);
when(mFeatureFactory.powerUsageFeatureProvider.isTypeService(Matchers.<BatterySipper>any()))
.thenReturn(true);
assertThat(mFragment.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testSetUsageSummary_timeLessThanOneMinute_doNotSetSummary() {
public void testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary() {
final long usageTimeMs = 59 * DateUtils.SECOND_IN_MILLIS;
mFragment.setUsageSummary(mPreference, "", usageTimeMs);