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

Commit e01cead9 authored by Michael Wachenschwanz's avatar Michael Wachenschwanz
Browse files

Revert "Add config to control BatteryStats reset logic"

Revert submission 21485850

Reason for revert: introduced deadlock

Reverted changes: /q/submissionid:21485850

Change-Id: Id3f920a130bc20d59abefc0a2f018873b08415ed
parent 806d71bf
Loading
Loading
Loading
Loading
+16 −118
Original line number Diff line number Diff line
@@ -387,81 +387,6 @@ 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 Runnable mDeferSetCharging = new Runnable() {
@@ -1534,10 +1459,6 @@ public class BatteryStatsImpl extends BatteryStats {
    @GuardedBy("this")
    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,
     * recording their times when on-battery (regardless of screen state).
@@ -1704,13 +1625,12 @@ public class BatteryStatsImpl extends BatteryStats {
    public BatteryStatsImpl(Clock clock, File historyDirectory) {
        init(clock);
        mStartClockTimeMs = clock.currentTimeMillis();
        mCheckinFile = null;
        mDailyFile = null;
        if (historyDirectory == null) {
            mCheckinFile = null;
            mStatsFile = null;
            mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
        } else {
            mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
            mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
            mBatteryStatsHistory = new BatteryStatsHistory(this, historyDirectory, mHistoryBuffer);
        }
@@ -12627,15 +12547,6 @@ public class BatteryStatsImpl extends BatteryStats {
        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,
     * keeping a separate account of threads receiving incoming binder calls.
@@ -15593,32 +15504,6 @@ public class BatteryStatsImpl extends BatteryStats {
        mSystemReady = true;
    }
    @GuardedBy("this")
    private boolean shouldResetOnUnplugLocked(int batteryStatus, int batteryLevel) {
        if (mNoAutoReset) return false;
        if (!mSystemReady) 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")
    protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
            final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
@@ -15631,10 +15516,23 @@ public class BatteryStatsImpl extends BatteryStats {
        final long realtimeUs = mSecRealtime * 1000;
        final int screenState = mScreenState;
        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;
            if (shouldResetOnUnplugLocked(oldStatus, level)) {
            if (!mNoAutoReset && mSystemReady
                    && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
                    || level >= 90
                    || (mDischargeCurrentLevel < 20 && level >= 80)
                    || getHighDischargeAmountSinceCharge() >= 200)) {
                Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
                        + " dischargeLevel=" + mDischargePlugLevel
                        + " dischargeLevel=" + mDischargeCurrentLevel
                        + " lowAmount=" + getLowDischargeAmountSinceCharge()
                        + " highAmount=" + getHighDischargeAmountSinceCharge());
                // Before we write, collect a snapshot of the final aggregated
+0 −5
Original line number Diff line number Diff line
@@ -6092,9 +6092,4 @@

    <!-- Whether to show weather on the lock screen by default. -->
    <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>
+0 −3
Original line number Diff line number Diff line
@@ -4903,7 +4903,4 @@

  <!-- Whether to show weather on the lockscreen by default. -->
  <java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />

  <java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
  <java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
</resources>
+0 −298
Original line number Diff line number Diff line
/*
 * 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.internal.os;


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());
    }
}
+0 −10
Original line number Diff line number Diff line
@@ -356,16 +356,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
        mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
                com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
        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();

        if (BATTERY_USAGE_STORE_ENABLED) {