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

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

Add PowerStatsAggregator to aggregate power stats

This aggregator iterates over battery history and
aggregates power stats on  a per battery state,
per screen state, per UID, per proc state basis.

Bug: 292296001
Test: atest FrameworksServicesTests:BatteryStatsTests
Test: adb shell dumpsys batterystats --aggregated
Change-Id: I1d2ca51254c1e46eedb4e8bb88673e0c65501769
parent a1069e55
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -41,6 +41,10 @@ public class MultiStateStats {
            this.mTracked = tracked;
            this.mTracked = tracked;
            this.mLabels = labels;
            this.mLabels = labels;
        }
        }

        public boolean isTracked() {
            return mTracked;
        }
    }
    }


    /**
    /**
+38 −17
Original line number Original line Diff line number Diff line
@@ -21,12 +21,14 @@ import android.annotation.Nullable;
import android.os.BatteryConsumer;
import android.os.BatteryConsumer;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseArray;


import java.util.Arrays;
import java.util.Arrays;
import java.util.Objects;


/**
/**
 * Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for
 * Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for
@@ -74,16 +76,16 @@ public final class PowerStats {
         * Extra parameters specific to the power component, e.g. the availability of power
         * Extra parameters specific to the power component, e.g. the availability of power
         * monitors.
         * monitors.
         */
         */
        public final Bundle extras;
        public final PersistableBundle extras;


        public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId,
        public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId,
                int statsArrayLength, int uidStatsArrayLength, @NonNull Bundle extras) {
                int statsArrayLength, int uidStatsArrayLength, @NonNull PersistableBundle extras) {
            this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId),
            this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId),
                    statsArrayLength, uidStatsArrayLength, extras);
                    statsArrayLength, uidStatsArrayLength, extras);
        }
        }


        public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
        public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
                int uidStatsArrayLength, Bundle extras) {
                int uidStatsArrayLength, PersistableBundle extras) {
            if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
            if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
                throw new IllegalArgumentException(
                throw new IllegalArgumentException(
                        "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
                        "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
@@ -134,11 +136,30 @@ public final class PowerStats {
                    (firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT;
                    (firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT;
            int powerComponentId = parcel.readInt();
            int powerComponentId = parcel.readInt();
            String name = parcel.readString();
            String name = parcel.readString();
            Bundle extras = parcel.readBundle(PowerStats.class.getClassLoader());
            PersistableBundle extras = parcel.readPersistableBundle();
            return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
            return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
                    extras);
                    extras);
        }
        }


        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Descriptor)) return false;
            Descriptor that = (Descriptor) o;
            return powerComponentId == that.powerComponentId
                   && statsArrayLength == that.statsArrayLength
                   && uidStatsArrayLength == that.uidStatsArrayLength
                   && Objects.equals(name, that.name)
                   && extras.size() == that.extras.size()        // Unparcel the Parcel if not yet
                   && Bundle.kindofEquals(extras,
                    that.extras);  // Since the Parcel is now unparceled, do a deep comparison
        }

        @Override
        public int hashCode() {
            return Objects.hash(powerComponentId);
        }

        @Override
        @Override
        public String toString() {
        public String toString() {
            if (extras != null) {
            if (extras != null) {
+8 −8
Original line number Original line Diff line number Diff line
@@ -19,8 +19,8 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;


import android.os.BatteryConsumer;
import android.os.BatteryConsumer;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcel;
import android.os.PersistableBundle;


import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.runner.AndroidJUnit4;
@@ -39,7 +39,7 @@ public class PowerStatsTest {
    @Before
    @Before
    public void setup() {
    public void setup() {
        mRegistry = new PowerStats.DescriptorRegistry();
        mRegistry = new PowerStats.DescriptorRegistry();
        Bundle extras = new Bundle();
        PersistableBundle extras = new PersistableBundle();
        extras.putBoolean("hasPowerMonitor", true);
        extras.putBoolean("hasPowerMonitor", true);
        mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras);
        mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras);
        mRegistry.register(mDescriptor);
        mRegistry.register(mDescriptor);
@@ -86,7 +86,7 @@ public class PowerStatsTest {
    @Test
    @Test
    public void parceling_unrecognizedPowerComponent() {
    public void parceling_unrecognizedPowerComponent() {
        PowerStats stats = new PowerStats(
        PowerStats stats = new PowerStats(
                new PowerStats.Descriptor(777, "luck", 3, 2, new Bundle()));
                new PowerStats.Descriptor(777, "luck", 3, 2, new PersistableBundle()));
        stats.durationMs = 1234;
        stats.durationMs = 1234;


        Parcel parcel = Parcel.obtain();
        Parcel parcel = Parcel.obtain();
+7 −0
Original line number Original line Diff line number Diff line
@@ -2631,6 +2631,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
        mStats.dumpStatsSample(pw);
        mStats.dumpStatsSample(pw);
    }
    }


    private void dumpAggregatedStats(PrintWriter pw) {
        mStats.dumpAggregatedStats(pw, /* startTime */ 0, /* endTime */0);
    }

    private void dumpMeasuredEnergyStats(PrintWriter pw) {
    private void dumpMeasuredEnergyStats(PrintWriter pw) {
        // Wait for the completion of pending works if there is any
        // Wait for the completion of pending works if there is any
        awaitCompletion();
        awaitCompletion();
@@ -2875,6 +2879,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                } else if ("--sample".equals(arg)) {
                } else if ("--sample".equals(arg)) {
                    dumpStatsSample(pw);
                    dumpStatsSample(pw);
                    return;
                    return;
                } else if ("--aggregated".equals(arg)) {
                    dumpAggregatedStats(pw);
                    return;
                } else if ("-a".equals(arg)) {
                } else if ("-a".equals(arg)) {
                    flags |= BatteryStats.DUMP_VERBOSE;
                    flags |= BatteryStats.DUMP_VERBOSE;
                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
+147 −0
Original line number Original line 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.server.power.stats;

import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.IndentingPrintWriter;

import com.android.internal.os.PowerStats;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * This class represents aggregated power stats for a variety of power components (CPU, WiFi,
 * etc) covering a specific period of power usage history.
 */
class AggregatedPowerStats {
    private final PowerComponentAggregatedPowerStats[] mPowerComponentStats;

    @CurrentTimeMillisLong
    private long mStartTime;

    @DurationMillisLong
    private long mDurationMs;

    AggregatedPowerStats(PowerComponentAggregatedPowerStats... powerComponentAggregatedPowerStats) {
        this.mPowerComponentStats = powerComponentAggregatedPowerStats;
    }

    void setStartTime(@CurrentTimeMillisLong long startTime) {
        mStartTime = startTime;
    }

    @CurrentTimeMillisLong
    public long getStartTime() {
        return mStartTime;
    }

    void setDuration(long durationMs) {
        mDurationMs = durationMs;
    }

    @DurationMillisLong
    public long getDuration() {
        return mDurationMs;
    }

    PowerComponentAggregatedPowerStats getPowerComponentStats(int powerComponentId) {
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            if (stats.powerComponentId == powerComponentId) {
                return stats;
            }
        }
        return null;
    }

    void setDeviceState(@PowerStatsAggregator.TrackedState int stateId, int state, long time) {
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            stats.setState(stateId, state, time);
        }
    }

    void setUidState(int uid, @PowerStatsAggregator.TrackedState int stateId, int state,
            long time) {
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            stats.setUidState(uid, stateId, state, time);
        }
    }

    boolean isCompatible(PowerStats powerStats) {
        int powerComponentId = powerStats.descriptor.powerComponentId;
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            if (stats.powerComponentId == powerComponentId && !stats.isCompatible(powerStats)) {
                return false;
            }
        }
        return true;
    }

    void addPowerStats(PowerStats powerStats, long time) {
        int powerComponentId = powerStats.descriptor.powerComponentId;
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            if (stats.powerComponentId == powerComponentId) {
                stats.addPowerStats(powerStats, time);
            }
        }
    }

    void reset() {
        mStartTime = 0;
        mDurationMs = 0;
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            stats.reset();
        }
    }

    void dump(PrintWriter pw) {
        IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
        ipw.print("Start time: ");
        ipw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartTime));
        ipw.print(" duration: ");
        ipw.print(mDurationMs);
        ipw.println();

        ipw.println("Device");
        ipw.increaseIndent();
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            stats.dumpDevice(ipw);
        }
        ipw.decreaseIndent();

        Set<Integer> uids = new HashSet<>();
        for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
            stats.collectUids(uids);
        }

        Integer[] allUids = uids.toArray(new Integer[uids.size()]);
        Arrays.sort(allUids);
        for (int uid : allUids) {
            ipw.println(UserHandle.formatUid(uid));
            ipw.increaseIndent();
            for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
                stats.dumpUid(ipw, uid);
            }
            ipw.decreaseIndent();
        }
    }
}
Loading