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

Commit f855a62e authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Fix handling of inconsistent PowerStats HAL data

Bug: 324758228
Test: atest --host PowerStatsTestsRavenwood
Change-Id: I603b7915d7d21924dd1eb9ae415a30f0f298a339
parent 9e3b6391
Loading
Loading
Loading
Loading
+11 −14
Original line number Diff line number Diff line
@@ -15428,17 +15428,18 @@ public class BatteryStatsImpl extends BatteryStats {
            mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
        }
        final boolean compatibleConfig;
        if (supportedStandardBuckets != null) {
            final EnergyConsumerStats.Config config = new EnergyConsumerStats.Config(
                    supportedStandardBuckets, customBucketNames,
                    SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS,
                    getBatteryConsumerProcessStateNames());
            if (mEnergyConsumerStatsConfig == null) {
                compatibleConfig = true;
            } else {
                compatibleConfig = mEnergyConsumerStatsConfig.isCompatible(config);
            if (mEnergyConsumerStatsConfig != null
                    &&  !mEnergyConsumerStatsConfig.isCompatible(config)) {
                // Supported power buckets changed since last boot.
                // Existing data is no longer reliable.
                resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
                        RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
            }
            mEnergyConsumerStatsConfig = config;
@@ -15454,18 +15455,14 @@ public class BatteryStatsImpl extends BatteryStats {
                mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
            }
        } else {
            compatibleConfig = (mEnergyConsumerStatsConfig == null);
            if (mEnergyConsumerStatsConfig != null) {
                // EnergyConsumer no longer supported, wipe out the existing data.
            mEnergyConsumerStatsConfig = null;
            mGlobalEnergyConsumerStats = null;
        }
        if (!compatibleConfig) {
            // Supported power buckets changed since last boot.
            // Existing data is no longer reliable.
                resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
                        RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
            }
            mEnergyConsumerStatsConfig = null;
            mGlobalEnergyConsumerStats = null;
        }
    }
    @GuardedBy("this")
+57 −0
Original line number Diff line number Diff line
@@ -18,12 +18,17 @@ package com.android.server.power.stats;

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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -34,6 +39,7 @@ import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseLongArray;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -516,6 +522,57 @@ public class BatteryUsageStatsProviderTest {
        mMockClock.realtime = timeMs;
    }

    @Test
    public void saveBatteryUsageStatsOnReset_incompatibleEnergyConsumers() throws Throwable {
        MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
        batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
        int componentId0 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
        int componentId1 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1;

        synchronized (batteryStats) {
            batteryStats.getUidStatsLocked(APP_UID);

            SparseLongArray uidEnergies = new SparseLongArray();
            uidEnergies.put(APP_UID, 30_000_000);
            batteryStats.updateCustomEnergyConsumerStatsLocked(0, 100_000_000, uidEnergies);
            batteryStats.updateCustomEnergyConsumerStatsLocked(1, 200_000_000, uidEnergies);
        }

        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);

        PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
        doAnswer(invocation -> {
            BatteryUsageStats stats = invocation.getArgument(1);
            AggregateBatteryConsumer device = stats.getAggregateBatteryConsumer(
                    BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
            assertThat(device.getCustomPowerComponentName(componentId0)).isEqualTo("FOO");
            assertThat(device.getCustomPowerComponentName(componentId1)).isEqualTo("BAR");
            assertThat(device.getConsumedPowerForCustomComponent(componentId0))
                    .isWithin(PRECISION).of(27.77777);
            assertThat(device.getConsumedPowerForCustomComponent(componentId1))
                    .isWithin(PRECISION).of(55.55555);

            UidBatteryConsumer uid = stats.getUidBatteryConsumers().get(0);
            assertThat(uid.getConsumedPowerForCustomComponent(componentId0))
                    .isWithin(PRECISION).of(8.33333);
            assertThat(uid.getConsumedPowerForCustomComponent(componentId1))
                    .isWithin(PRECISION).of(8.33333);
            return null;
        }).when(powerStatsStore).storeBatteryUsageStats(anyLong(), any());

        mStatsRule.getBatteryStats().saveBatteryUsageStatsOnReset(provider, powerStatsStore);

        // Make an incompatible change of supported energy components.  This will trigger
        // a BatteryStats reset, which will generate a snapshot of battery stats.
        mStatsRule.initMeasuredEnergyStatsLocked(
                new String[]{"COMPONENT1"});

        mStatsRule.waitForBackgroundThread();

        verify(powerStatsStore).storeBatteryUsageStats(anyLong(), any());
    }

    @Test
    public void testAggregateBatteryStats_incompatibleSnapshot() {
        MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+28 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.power.stats;

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

import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -28,6 +30,7 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UidBatteryConsumer;
@@ -74,6 +77,7 @@ public class BatteryUsageStatsRule implements TestRule {
    private NetworkStats mNetworkStats;
    private boolean[] mSupportedStandardBuckets;
    private String[] mCustomPowerComponentNames;
    private Throwable mThrowable;

    public BatteryUsageStatsRule() {
        this(0, null);
@@ -270,6 +274,7 @@ public class BatteryUsageStatsRule implements TestRule {
            public void evaluate() throws Throwable {
                before();
                base.evaluate();
                after();
            }
        };
    }
@@ -277,6 +282,9 @@ public class BatteryUsageStatsRule implements TestRule {
    private void before() {
        lateInitBatteryStats();
        HandlerThread bgThread = new HandlerThread("bg thread");
        bgThread.setUncaughtExceptionHandler((thread, throwable)-> {
            mThrowable = throwable;
        });
        bgThread.start();
        mHandler = new Handler(bgThread.getLooper());
        mBatteryStats.setHandler(mHandler);
@@ -285,6 +293,26 @@ public class BatteryUsageStatsRule implements TestRule {
        mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
    }

    private void after() throws Throwable {
        if (mHandler != null) {
            waitForBackgroundThread();
        }
    }

    public void waitForBackgroundThread() throws Throwable {
        if (mThrowable != null) {
            throw mThrowable;
        }

        ConditionVariable done = new ConditionVariable();
        mHandler.post(done::open);
        assertThat(done.block(10000)).isTrue();

        if (mThrowable != null) {
            throw mThrowable;
        }
    }

    public PowerProfile getPowerProfile() {
        return mPowerProfile;
    }