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

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

Check custom component names before aggregating BatteryUsageStats snapshots

If the lists of custom power components do not match, a crash will occur.
Instead of causing a crash, simply skip incompatible snapshots.

Bug: 196040329
Test: atest FrameworksCoreTests:com.android.internal.os.BatteryUsageStatsProviderTest

Change-Id: I87ba605371a5f3119dcff33f6109e94ee46ab57d
parent 6fd7e85c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -270,6 +270,16 @@ public final class BatteryUsageStats implements Parcelable {
        return mUserBatteryConsumers;
    }

    /**
     * Returns the names of custom power components in order, so the first name in the array
     * corresponds to the custom componentId
     * {@link BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID}.
     */
    @NonNull
    public String[] getCustomPowerComponentNames() {
        return mCustomPowerComponentNames;
    }

    /**
     * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s.
     */
+11 −2
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@@ -234,8 +235,9 @@ public class BatteryUsageStatsProvider {
        final boolean includePowerModels = (query.getFlags()
                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;

        final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
        final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
                mStats.getCustomEnergyConsumerNames(), includePowerModels);
                customEnergyConsumerNames, includePowerModels);
        if (mBatteryUsageStatsStore == null) {
            Log.e(TAG, "BatteryUsageStatsStore is unavailable");
            return builder.build();
@@ -247,7 +249,14 @@ public class BatteryUsageStatsProvider {
                final BatteryUsageStats snapshot =
                        mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
                if (snapshot != null) {
                    if (Arrays.equals(snapshot.getCustomPowerComponentNames(),
                            customEnergyConsumerNames)) {
                        builder.add(snapshot);
                    } else {
                        Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
                                + "custom power components: "
                                + Arrays.toString(snapshot.getCustomPowerComponentNames()));
                    }
                }
            }
        }
+3 −3
Original line number Diff line number Diff line
@@ -504,7 +504,7 @@ public class BatteryStatsNoteTest extends TestCase {
    public void testUpdateDisplayMeasuredEnergyStatsLocked() {
        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
        final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        bi.initMeasuredEnergyStats();
        bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});

        clocks.realtime = 0;
        int screen = Display.STATE_OFF;
@@ -589,7 +589,7 @@ public class BatteryStatsNoteTest extends TestCase {
    public void testUpdateCustomMeasuredEnergyStatsLocked_neverCalled() {
        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
        final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        bi.initMeasuredEnergyStats();
        bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
        bi.setOnBatteryInternal(true);

        final int uid1 = 11500;
@@ -603,7 +603,7 @@ public class BatteryStatsNoteTest extends TestCase {
    public void testUpdateCustomMeasuredEnergyStatsLocked() {
        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
        final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        bi.initMeasuredEnergyStats();
        bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});

        final int bucketA = 0; // Custom bucket 0
        final int bucketB = 1; // Custom bucket 1
+36 −0
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.internal.os;

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

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.content.Context;
import android.os.BatteryConsumer;
@@ -263,6 +266,39 @@ public class BatteryUsageStatsProviderTest {
                .of(180.0);
    }

    @Test
    public void testAggregateBatteryStats_incompatibleSnapshot() {
        Context context = InstrumentationRegistry.getContext();
        MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
        batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});

        BatteryUsageStatsStore batteryUsageStatsStore = mock(BatteryUsageStatsStore.class);

        when(batteryUsageStatsStore.listBatteryUsageStatsTimestamps())
                .thenReturn(new long[]{1000, 2000});

        when(batteryUsageStatsStore.loadBatteryUsageStats(1000)).thenReturn(
                new BatteryUsageStats.Builder(batteryStats.getCustomEnergyConsumerNames())
                        .setStatsDuration(1234).build());

        // Add a snapshot, with a different set of custom power components.  It should
        // be skipped by the aggregation.
        when(batteryUsageStatsStore.loadBatteryUsageStats(2000)).thenReturn(
                new BatteryUsageStats.Builder(new String[]{"different"})
                        .setStatsDuration(4321).build());

        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
                batteryStats, batteryUsageStatsStore);

        BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
                .aggregateSnapshots(0, 3000)
                .build();
        final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
        assertThat(stats.getCustomPowerComponentNames())
                .isEqualTo(batteryStats.getCustomEnergyConsumerNames());
        assertThat(stats.getStatsDuration()).isEqualTo(1234);
    }

    private static class TestHandler extends Handler {
        TestHandler() {
            super(Looper.getMainLooper());
+1 −2
Original line number Diff line number Diff line
@@ -57,11 +57,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
        this(new MockClock());
    }

    public void initMeasuredEnergyStats() {
    public void initMeasuredEnergyStats(String[] customBucketNames) {
        final boolean[] supportedStandardBuckets =
                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
        Arrays.fill(supportedStandardBuckets, true);
        final String[] customBucketNames = {"FOO", "BAR"};
        mGlobalMeasuredEnergyStats =
                new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
    }