Add config to control BatteryStats reset logic
Also, fix the significant charge reset logic and add BatteryStats reset tests. Fix: 269538224 Bug: 256919205 Test: atest BatteryStatsResetTest Change-Id: Id4a6dd22708ddee38dd6c10ceb6c81f6a7bfb44f
This commit is contained in:
@@ -6322,4 +6322,9 @@
|
|||||||
|
|
||||||
<!-- Whether to show weather on the lock screen by default. -->
|
<!-- Whether to show weather on the lock screen by default. -->
|
||||||
<bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
|
<bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
|
||||||
|
|
||||||
|
<!-- Whether to reset Battery Stats on unplug when the battery level is high. -->
|
||||||
|
<bool name="config_batteryStatsResetOnUnplugHighBatteryLevel">true</bool>
|
||||||
|
<!-- Whether to reset Battery Stats on unplug if the battery was significantly charged -->
|
||||||
|
<bool name="config_batteryStatsResetOnUnplugAfterSignificantCharge">true</bool>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -4960,4 +4960,7 @@
|
|||||||
|
|
||||||
<!-- Whether to show weather on the lockscreen by default. -->
|
<!-- Whether to show weather on the lockscreen by default. -->
|
||||||
<java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
|
<java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
|
||||||
|
|
||||||
|
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
|
||||||
|
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -370,6 +370,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
|
|||||||
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
|
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
|
||||||
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
|
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
|
||||||
mStats.setPowerProfileLocked(mPowerProfile);
|
mStats.setPowerProfileLocked(mPowerProfile);
|
||||||
|
|
||||||
|
final boolean resetOnUnplugHighBatteryLevel = context.getResources().getBoolean(
|
||||||
|
com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
|
||||||
|
final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean(
|
||||||
|
com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
|
||||||
|
mStats.setBatteryStatsConfig(
|
||||||
|
new BatteryStatsImpl.BatteryStatsConfig.Builder()
|
||||||
|
.setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
|
||||||
|
.setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
|
||||||
|
.build());
|
||||||
mStats.startTrackingSystemServerCpuTime();
|
mStats.startTrackingSystemServerCpuTime();
|
||||||
|
|
||||||
if (BATTERY_USAGE_STORE_ENABLED) {
|
if (BATTERY_USAGE_STORE_ENABLED) {
|
||||||
|
|||||||
@@ -423,6 +423,81 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Provide BatteryStatsImpl configuration choices */
|
||||||
|
public static class BatteryStatsConfig {
|
||||||
|
static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0;
|
||||||
|
static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1;
|
||||||
|
|
||||||
|
private final int mFlags;
|
||||||
|
|
||||||
|
private BatteryStatsConfig(Builder builder) {
|
||||||
|
int flags = 0;
|
||||||
|
if (builder.mResetOnUnplugHighBatteryLevel) {
|
||||||
|
flags |= RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG;
|
||||||
|
}
|
||||||
|
if (builder.mResetOnUnplugAfterSignificantCharge) {
|
||||||
|
flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
|
||||||
|
}
|
||||||
|
mFlags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a BatteryStats reset should occur on unplug when the battery level is
|
||||||
|
* high.
|
||||||
|
*/
|
||||||
|
boolean shouldResetOnUnplugHighBatteryLevel() {
|
||||||
|
return (mFlags & RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG)
|
||||||
|
== RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a BatteryStats reset should occur on unplug if the battery charge a
|
||||||
|
* significant amount since it has been plugged in.
|
||||||
|
*/
|
||||||
|
boolean shouldResetOnUnplugAfterSignificantCharge() {
|
||||||
|
return (mFlags & RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG)
|
||||||
|
== RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for BatteryStatsConfig
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private boolean mResetOnUnplugHighBatteryLevel;
|
||||||
|
private boolean mResetOnUnplugAfterSignificantCharge;
|
||||||
|
public Builder() {
|
||||||
|
mResetOnUnplugHighBatteryLevel = true;
|
||||||
|
mResetOnUnplugAfterSignificantCharge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the BatteryStatsConfig.
|
||||||
|
*/
|
||||||
|
public BatteryStatsConfig build() {
|
||||||
|
return new BatteryStatsConfig(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether a BatteryStats reset should occur on unplug when the battery level is
|
||||||
|
* high.
|
||||||
|
*/
|
||||||
|
public Builder setResetOnUnplugHighBatteryLevel(boolean reset) {
|
||||||
|
mResetOnUnplugHighBatteryLevel = reset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether a BatteryStats reset should occur on unplug if the battery charge a
|
||||||
|
* significant amount since it has been plugged in.
|
||||||
|
*/
|
||||||
|
public Builder setResetOnUnplugAfterSignificantCharge(boolean reset) {
|
||||||
|
mResetOnUnplugAfterSignificantCharge = reset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private final PlatformIdleStateCallback mPlatformIdleStateCallback;
|
private final PlatformIdleStateCallback mPlatformIdleStateCallback;
|
||||||
|
|
||||||
private final Runnable mDeferSetCharging = new Runnable() {
|
private final Runnable mDeferSetCharging = new Runnable() {
|
||||||
@@ -1479,6 +1554,10 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
protected final Constants mConstants;
|
protected final Constants mConstants;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@GuardedBy("this")
|
||||||
|
protected BatteryStatsConfig mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Holds a SamplingTimer associated with each Resource Power Manager state and voter,
|
* Holds a SamplingTimer associated with each Resource Power Manager state and voter,
|
||||||
* recording their times when on-battery (regardless of screen state).
|
* recording their times when on-battery (regardless of screen state).
|
||||||
@@ -1646,12 +1725,13 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
mHandler = null;
|
mHandler = null;
|
||||||
mConstants = new Constants(mHandler);
|
mConstants = new Constants(mHandler);
|
||||||
mStartClockTimeMs = clock.currentTimeMillis();
|
mStartClockTimeMs = clock.currentTimeMillis();
|
||||||
mCheckinFile = null;
|
|
||||||
mDailyFile = null;
|
mDailyFile = null;
|
||||||
if (historyDirectory == null) {
|
if (historyDirectory == null) {
|
||||||
|
mCheckinFile = null;
|
||||||
mStatsFile = null;
|
mStatsFile = null;
|
||||||
mHistory = new BatteryStatsHistory(mStepDetailsCalculator, mClock);
|
mHistory = new BatteryStatsHistory(mStepDetailsCalculator, mClock);
|
||||||
} else {
|
} else {
|
||||||
|
mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
|
||||||
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
|
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
|
||||||
mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
|
mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
|
||||||
mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
|
mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
|
||||||
@@ -10953,6 +11033,15 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
return mPowerProfile;
|
return mPowerProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects BatteryStatsConfig
|
||||||
|
*/
|
||||||
|
public void setBatteryStatsConfig(BatteryStatsConfig config) {
|
||||||
|
synchronized (this) {
|
||||||
|
mBatteryStatsConfig = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts tracking CPU time-in-state for threads of the system server process,
|
* Starts tracking CPU time-in-state for threads of the system server process,
|
||||||
* keeping a separate account of threads receiving incoming binder calls.
|
* keeping a separate account of threads receiving incoming binder calls.
|
||||||
@@ -14051,6 +14140,33 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
mRecordAllHistory = true;
|
mRecordAllHistory = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private boolean shouldResetOnUnplugLocked(int batteryStatus, int batteryLevel) {
|
||||||
|
if (mNoAutoReset) return false;
|
||||||
|
if (!mSystemReady) return false;
|
||||||
|
if (!mHistory.isResetEnabled()) return false;
|
||||||
|
if (mBatteryStatsConfig.shouldResetOnUnplugHighBatteryLevel()) {
|
||||||
|
// Allow resetting due to currently being at high battery level
|
||||||
|
if (batteryStatus == BatteryManager.BATTERY_STATUS_FULL) return true;
|
||||||
|
if (batteryLevel >= 90) return true;
|
||||||
|
}
|
||||||
|
if (mBatteryStatsConfig.shouldResetOnUnplugAfterSignificantCharge()) {
|
||||||
|
// Allow resetting after a significant charge (from a very low level to a now very
|
||||||
|
// high level).
|
||||||
|
if (mDischargePlugLevel < 20 && batteryLevel >= 80) return true;
|
||||||
|
}
|
||||||
|
if (getHighDischargeAmountSinceCharge() >= 200) {
|
||||||
|
// Reset the stats if battery got partially charged and discharged repeatedly without
|
||||||
|
// ever reaching the full charge.
|
||||||
|
// This reset is done in order to prevent stats sessions from going on forever.
|
||||||
|
// Exceedingly long battery sessions would lead to an overflow of
|
||||||
|
// data structures such as mWakeupReasonStats.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
|
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
|
||||||
final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
|
final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
|
||||||
@@ -14063,24 +14179,10 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
final long realtimeUs = mSecRealtime * 1000;
|
final long realtimeUs = mSecRealtime * 1000;
|
||||||
final int screenState = mScreenState;
|
final int screenState = mScreenState;
|
||||||
if (onBattery) {
|
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).
|
|
||||||
// Also, we will reset the stats if battery got partially charged
|
|
||||||
// and discharged repeatedly without ever reaching the full charge.
|
|
||||||
// This reset is done in order to prevent stats sessions from going on forever.
|
|
||||||
// Exceedingly long battery sessions would lead to an overflow of
|
|
||||||
// data structures such as mWakeupReasonStats.
|
|
||||||
boolean reset = false;
|
boolean reset = false;
|
||||||
if (!mNoAutoReset && mSystemReady
|
if (shouldResetOnUnplugLocked(oldStatus, level)) {
|
||||||
&& (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|
|
||||||
|| level >= 90
|
|
||||||
|| (mDischargeCurrentLevel < 20 && level >= 80)
|
|
||||||
|| getHighDischargeAmountSinceCharge() >= 200)
|
|
||||||
&& mHistory.isResetEnabled()) {
|
|
||||||
Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
|
Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
|
||||||
+ " dischargeLevel=" + mDischargeCurrentLevel
|
+ " dischargeLevel=" + mDischargePlugLevel
|
||||||
+ " lowAmount=" + getLowDischargeAmountSinceCharge()
|
+ " lowAmount=" + getLowDischargeAmountSinceCharge()
|
||||||
+ " highAmount=" + getHighDischargeAmountSinceCharge());
|
+ " highAmount=" + getHighDischargeAmountSinceCharge());
|
||||||
// Before we write, collect a snapshot of the final aggregated
|
// Before we write, collect a snapshot of the final aggregated
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.power.stats;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.BatteryManager;
|
||||||
|
|
||||||
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class BatteryStatsResetTest {
|
||||||
|
|
||||||
|
private static final int BATTERY_NOMINAL_VOLTAGE_MV = 3700;
|
||||||
|
private static final int BATTERY_CAPACITY_UAH = 4_000_000;
|
||||||
|
private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100;
|
||||||
|
|
||||||
|
private MockClock mMockClock;
|
||||||
|
private MockBatteryStatsImpl mBatteryStatsImpl;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Battery status. Must be one of the following:
|
||||||
|
* {@link BatteryManager#BATTERY_STATUS_UNKNOWN}
|
||||||
|
* {@link BatteryManager#BATTERY_STATUS_CHARGING}
|
||||||
|
* {@link BatteryManager#BATTERY_STATUS_DISCHARGING}
|
||||||
|
* {@link BatteryManager#BATTERY_STATUS_NOT_CHARGING}
|
||||||
|
* {@link BatteryManager#BATTERY_STATUS_FULL}
|
||||||
|
*/
|
||||||
|
private int mBatteryStatus;
|
||||||
|
/**
|
||||||
|
* Battery health. Must be one of the following:
|
||||||
|
* {@link BatteryManager#BATTERY_HEALTH_UNKNOWN}
|
||||||
|
* {@link BatteryManager#BATTERY_HEALTH_GOOD}
|
||||||
|
* {@link BatteryManager#BATTERY_HEALTH_OVERHEAT}
|
||||||
|
* {@link BatteryManager#BATTERY_HEALTH_DEAD}
|
||||||
|
* {@link BatteryManager#BATTERY_HEALTH_OVER_VOLTAGE}
|
||||||
|
* {@link BatteryManager#BATTERY_HEALTH_UNSPECIFIED_FAILURE}
|
||||||
|
* {@link BatteryManager#BATTERY_HEALTH_COLD}
|
||||||
|
*/
|
||||||
|
private int mBatteryHealth;
|
||||||
|
/**
|
||||||
|
* Battery plug type. Can be the union of any number of the following flags:
|
||||||
|
* {@link BatteryManager#BATTERY_PLUGGED_AC}
|
||||||
|
* {@link BatteryManager#BATTERY_PLUGGED_USB}
|
||||||
|
* {@link BatteryManager#BATTERY_PLUGGED_WIRELESS}
|
||||||
|
* {@link BatteryManager#BATTERY_PLUGGED_DOCK}
|
||||||
|
*
|
||||||
|
* Zero means the device is unplugged.
|
||||||
|
*/
|
||||||
|
private int mBatteryPlugType;
|
||||||
|
private int mBatteryLevel;
|
||||||
|
private int mBatteryTemp;
|
||||||
|
private int mBatteryVoltageMv;
|
||||||
|
private int mBatteryChargeUah;
|
||||||
|
private int mBatteryChargeFullUah;
|
||||||
|
private long mBatteryChargeTimeToFullSeconds;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
final Context context = InstrumentationRegistry.getContext();
|
||||||
|
|
||||||
|
mMockClock = new MockClock();
|
||||||
|
mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, context.getFilesDir());
|
||||||
|
mBatteryStatsImpl.onSystemReady();
|
||||||
|
|
||||||
|
|
||||||
|
// Set up the battery state. Start off with a fully charged plugged in battery.
|
||||||
|
mBatteryStatus = BatteryManager.BATTERY_STATUS_FULL;
|
||||||
|
mBatteryHealth = BatteryManager.BATTERY_HEALTH_GOOD;
|
||||||
|
mBatteryPlugType = BatteryManager.BATTERY_PLUGGED_USB;
|
||||||
|
mBatteryLevel = 100;
|
||||||
|
mBatteryTemp = 70; // Arbitrary reasonable temperature.
|
||||||
|
mBatteryVoltageMv = BATTERY_NOMINAL_VOLTAGE_MV;
|
||||||
|
mBatteryChargeUah = BATTERY_CAPACITY_UAH;
|
||||||
|
mBatteryChargeFullUah = BATTERY_CAPACITY_UAH;
|
||||||
|
mBatteryChargeTimeToFullSeconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResetOnUnplug_highBatteryLevel() {
|
||||||
|
mBatteryStatsImpl.setBatteryStatsConfig(
|
||||||
|
new BatteryStatsImpl.BatteryStatsConfig.Builder()
|
||||||
|
.setResetOnUnplugHighBatteryLevel(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
long expectedResetTimeUs = 0;
|
||||||
|
|
||||||
|
unplugBattery();
|
||||||
|
dischargeToLevel(60);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(80);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur until battery level above 90.
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(95);
|
||||||
|
// Reset should not occur until unplug.
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should occur on unplug now that battery level is high (>=90)
|
||||||
|
expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
// disable high battery level reset on unplug.
|
||||||
|
mBatteryStatsImpl.setBatteryStatsConfig(
|
||||||
|
new BatteryStatsImpl.BatteryStatsConfig.Builder()
|
||||||
|
.setResetOnUnplugHighBatteryLevel(false)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
dischargeToLevel(60);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(95);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur since the high battery level logic has been disabled.
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResetOnUnplug_significantCharge() {
|
||||||
|
mBatteryStatsImpl.setBatteryStatsConfig(
|
||||||
|
new BatteryStatsImpl.BatteryStatsConfig.Builder()
|
||||||
|
.setResetOnUnplugAfterSignificantCharge(true)
|
||||||
|
.build());
|
||||||
|
long expectedResetTimeUs = 0;
|
||||||
|
|
||||||
|
unplugBattery();
|
||||||
|
// Battery level dropped below 20%.
|
||||||
|
dischargeToLevel(15);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(50);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur until battery level above 80
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(85);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur because the charge session did not go from 20% to 80%
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
// Battery level dropped below 20%.
|
||||||
|
dischargeToLevel(15);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(85);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should occur after significant charge amount.
|
||||||
|
expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
// disable reset on unplug after significant charge.
|
||||||
|
mBatteryStatsImpl.setBatteryStatsConfig(
|
||||||
|
new BatteryStatsImpl.BatteryStatsConfig.Builder()
|
||||||
|
.setResetOnUnplugAfterSignificantCharge(false)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Battery level dropped below 20%.
|
||||||
|
dischargeToLevel(15);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(85);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur after significant charge amount.
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResetOnUnplug_manyPartialCharges() {
|
||||||
|
long expectedResetTimeUs = 0;
|
||||||
|
|
||||||
|
unplugBattery();
|
||||||
|
// Cumulative battery discharged: 60%.
|
||||||
|
dischargeToLevel(40);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(80);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
// Cumulative battery discharged: 100%.
|
||||||
|
dischargeToLevel(40);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(80);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
// Cumulative battery discharged: 140%.
|
||||||
|
dischargeToLevel(40);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(80);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
// Cumulative battery discharged: 180%.
|
||||||
|
dischargeToLevel(40);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(80);
|
||||||
|
unplugBattery();
|
||||||
|
// Reset should not occur
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
|
||||||
|
// Cumulative battery discharged: 220%.
|
||||||
|
dischargeToLevel(40);
|
||||||
|
|
||||||
|
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
|
||||||
|
chargeToLevel(80);
|
||||||
|
unplugBattery();
|
||||||
|
// Should reset after >200% of cumulative battery discharge
|
||||||
|
expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
|
||||||
|
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dischargeToLevel(int targetLevel) {
|
||||||
|
mBatteryStatus = BatteryManager.BATTERY_STATUS_DISCHARGING;
|
||||||
|
for (int level = mBatteryLevel - 1; level >= targetLevel; level--) {
|
||||||
|
prepareBatteryLevel(level);
|
||||||
|
incTimeMs(5000); // Arbitrary discharge rate.
|
||||||
|
updateBatteryState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void chargeToLevel(int targetLevel) {
|
||||||
|
mBatteryStatus = BatteryManager.BATTERY_STATUS_CHARGING;
|
||||||
|
for (int level = mBatteryLevel + 1; level <= targetLevel; level++) {
|
||||||
|
if (level >= 100) mBatteryStatus = BatteryManager.BATTERY_STATUS_FULL;
|
||||||
|
prepareBatteryLevel(level);
|
||||||
|
incTimeMs(BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL * 1000);
|
||||||
|
updateBatteryState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unplugBattery() {
|
||||||
|
mBatteryPlugType = 0;
|
||||||
|
updateBatteryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void plugBattery(int type) {
|
||||||
|
mBatteryPlugType |= type;
|
||||||
|
updateBatteryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareBatteryLevel(int level) {
|
||||||
|
mBatteryLevel = level;
|
||||||
|
mBatteryChargeUah = mBatteryLevel * mBatteryChargeFullUah / 100;
|
||||||
|
mBatteryChargeTimeToFullSeconds =
|
||||||
|
(100 - mBatteryLevel) * BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void incTimeMs(long milliseconds) {
|
||||||
|
mMockClock.realtime += milliseconds;
|
||||||
|
mMockClock.uptime += milliseconds / 2; // Arbitrary slower uptime accumulation
|
||||||
|
mMockClock.currentTime += milliseconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBatteryState() {
|
||||||
|
mBatteryStatsImpl.setBatteryStateLocked(mBatteryStatus, mBatteryHealth, mBatteryPlugType,
|
||||||
|
mBatteryLevel, mBatteryTemp, mBatteryVoltageMv, mBatteryChargeUah,
|
||||||
|
mBatteryChargeFullUah, mBatteryChargeTimeToFullSeconds,
|
||||||
|
mMockClock.elapsedRealtime(), mMockClock.uptimeMillis(),
|
||||||
|
mMockClock.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user