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

Commit 9b48407b authored by Dmitri Plotnikov's avatar Dmitri Plotnikov Committed by Android (Google) Code Review
Browse files

Merge changes I60f4b688,I1d783b10 into main

* changes:
  Add Power State and Screen State to BatteryUsageStats
  [Attempt 2] Add test for BatteryUsageStatsPerUid atom
parents c7077edb c2977948
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ public final class AggregateBatteryConsumer extends BatteryConsumer {

    @Override
    public void dump(PrintWriter pw, boolean skipEmptyComponents) {
        mPowerComponents.dump(pw, skipEmptyComponents);
        mPowerComponents.dump(pw, SCREEN_STATE_ANY, POWER_STATE_ANY, skipEmptyComponents);
    }

    @Override
+313 −132
Original line number Diff line number Diff line
@@ -19,14 +19,19 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.database.Cursor;
import android.database.CursorWindow;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Interface for objects containing battery attribution data.
@@ -192,7 +197,9 @@ public abstract class BatteryConsumer {
        sProcessStateNames[PROCESS_STATE_CACHED] = "cached";
    }

    private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
    private static final IntArray SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE;
    static {
        int[] supportedPowerComponents = {
                POWER_COMPONENT_CPU,
                POWER_COMPONENT_MOBILE_RADIO,
                POWER_COMPONENT_WIFI,
@@ -201,22 +208,95 @@ public abstract class BatteryConsumer {
                POWER_COMPONENT_VIDEO,
                POWER_COMPONENT_FLASHLIGHT,
                POWER_COMPONENT_CAMERA,
            POWER_COMPONENT_GNSS,
                POWER_COMPONENT_GNSS};
        Arrays.sort(supportedPowerComponents);
        SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = IntArray.wrap(supportedPowerComponents);
    };

    static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
    static final int COLUMN_COUNT = 1;

    /**
     * Identifiers of consumed power aggregations per SCREEN state.
     *
     * @hide
     */
    @IntDef(prefix = {"SCREEN_STATE_"}, value = {
            SCREEN_STATE_UNSPECIFIED,
            SCREEN_STATE_ANY,
            SCREEN_STATE_ON,
            SCREEN_STATE_OTHER,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ScreenState {
    }

    public static final int SCREEN_STATE_UNSPECIFIED = 0;
    public static final int SCREEN_STATE_ANY = SCREEN_STATE_UNSPECIFIED;
    public static final int SCREEN_STATE_ON = 1;
    public static final int SCREEN_STATE_OTHER = 2;  // Off, doze etc

    public static final int SCREEN_STATE_COUNT = 3;

    private static final String[] sScreenStateNames = new String[SCREEN_STATE_COUNT];

    static {
        // Assign individually to avoid future mismatch
        sScreenStateNames[SCREEN_STATE_UNSPECIFIED] = "unspecified";
        sScreenStateNames[SCREEN_STATE_ON] = "on";
        sScreenStateNames[SCREEN_STATE_OTHER] = "off/doze";
    }

    /**
     * Identifiers of consumed power aggregations per POWER state.
     *
     * @hide
     */
    @IntDef(prefix = {"POWER_STATE_"}, value = {
            POWER_STATE_UNSPECIFIED,
            POWER_STATE_ANY,
            POWER_STATE_BATTERY,
            POWER_STATE_OTHER,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface PowerState {
    }

    public static final int POWER_STATE_UNSPECIFIED = 0;
    public static final int POWER_STATE_ANY = POWER_STATE_UNSPECIFIED;
    public static final int POWER_STATE_BATTERY = 1;
    public static final int POWER_STATE_OTHER = 2;   // Plugged in, or on wireless charger, etc.

    public static final int POWER_STATE_COUNT = 3;

    private static final String[] sPowerStateNames = new String[POWER_STATE_COUNT];

    static {
        // Assign individually to avoid future mismatch
        sPowerStateNames[POWER_STATE_UNSPECIFIED] = "unspecified";
        sPowerStateNames[POWER_STATE_BATTERY] = "on battery";
        sPowerStateNames[POWER_STATE_OTHER] = "not on battery";
    }

    /**
     * Identifies power attribution dimensions that a caller is interested in.
     */
    public static final class Dimensions {
        public final @PowerComponent int powerComponent;
        public final @ProcessState int processState;
        public final @ScreenState int screenState;
        public final @PowerState int powerState;

        public Dimensions(@PowerComponent int powerComponent, @ProcessState int processState) {
            this(powerComponent, processState, SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED);
        }

        public Dimensions(int powerComponent, int processState) {
        public Dimensions(@PowerComponent int powerComponent, int processState,
                @ScreenState int screenState, @PowerState int powerState) {
            this.powerComponent = powerComponent;
            this.processState = processState;
            this.screenState = screenState;
            this.powerState = powerState;
        }

        @Override
@@ -234,6 +314,20 @@ public abstract class BatteryConsumer {
                sb.append("processState=").append(sProcessStateNames[processState]);
                dimensionSpecified = true;
            }
            if (screenState != SCREEN_STATE_ANY) {
                if (dimensionSpecified) {
                    sb.append(", ");
                }
                sb.append("screenState=").append(screenStateToString(screenState));
                dimensionSpecified = true;
            }
            if (powerState != POWER_STATE_ANY) {
                if (dimensionSpecified) {
                    sb.append(", ");
                }
                sb.append("powerState=").append(powerStateToString(powerState));
                dimensionSpecified = true;
            }
            if (!dimensionSpecified) {
                sb.append("any components and process states");
            }
@@ -242,7 +336,8 @@ public abstract class BatteryConsumer {
    }

    public static final Dimensions UNSPECIFIED_DIMENSIONS =
            new Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_ANY);
            new Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_ANY, SCREEN_STATE_ANY,
                    POWER_STATE_ANY);

    /**
     * Identifies power attribution dimensions that are captured by a data element of
@@ -258,52 +353,93 @@ public abstract class BatteryConsumer {
    public static final class Key {
        public final @PowerComponent int powerComponent;
        public final @ProcessState int processState;
        public final @ScreenState int screenState;
        public final @PowerState int powerState;

        final int mPowerModelColumnIndex;
        final int mPowerColumnIndex;
        final int mDurationColumnIndex;
        private String mShortString;

        private Key(int powerComponent, int processState, int powerModelColumnIndex,
        private Key(@PowerComponent int powerComponent, @ProcessState int processState,
                @ScreenState int screenState, @PowerState int powerState, int powerModelColumnIndex,
                int powerColumnIndex, int durationColumnIndex) {
            this.powerComponent = powerComponent;
            this.processState = processState;
            this.screenState = screenState;
            this.powerState = powerState;

            mPowerModelColumnIndex = powerModelColumnIndex;
            mPowerColumnIndex = powerColumnIndex;
            mDurationColumnIndex = durationColumnIndex;
        }

        /**
         * Returns true if this key should be included in an enumeration parameterized with
         * the supplied dimensions.
         */
        boolean matches(@PowerComponent int powerComponent, @ProcessState int processState,
                @ScreenState int screenState, @PowerState int powerState) {
            if (powerComponent != POWER_COMPONENT_ANY && this.powerComponent != powerComponent) {
                return false;
            }
            if (processState != PROCESS_STATE_ANY && this.processState != processState) {
                return false;
            }
            if (screenState != SCREEN_STATE_ANY && this.screenState != screenState) {
                return false;
            }
            if (powerState != POWER_STATE_ANY && this.powerState != powerState) {
                return false;
            }
            return true;
        }

        @SuppressWarnings("EqualsUnsafeCast")
        @Override
        public boolean equals(Object o) {
            // Skipping null and class check for performance
            final Key key = (Key) o;
            return powerComponent == key.powerComponent
                && processState == key.processState;
                    && processState == key.processState
                    && screenState == key.screenState
                    && powerState == key.powerState;
        }

        @Override
        public int hashCode() {
            int result = powerComponent;
            result = 31 * result + processState;
            result = 31 * result + screenState;
            result = 31 * result + powerState;
            return result;
        }

        /**
         * Returns a string suitable for use in dumpsys.
         */
        public String toShortString() {
            if (mShortString == null) {
        public static String toString(@PowerComponent int powerComponent,
                @ProcessState int processState, @ScreenState int screenState,
                @PowerState int powerState) {
            StringBuilder sb = new StringBuilder();
            sb.append(powerComponentIdToString(powerComponent));
            if (processState != PROCESS_STATE_UNSPECIFIED) {
                sb.append(':');
                sb.append(processStateToString(processState));
            }
                mShortString = sb.toString();
            if (screenState != SCREEN_STATE_UNSPECIFIED) {
                sb.append(":scr-");
                sb.append(sScreenStateNames[screenState]);
            }
            return mShortString;
            if (powerState != POWER_STATE_UNSPECIFIED) {
                sb.append(":pwr-");
                sb.append(sPowerStateNames[powerState]);
            }
            return sb.toString();
        }

        @Override
        public String toString() {
            return toString(powerComponent, processState, screenState, powerState);
        }
    }

@@ -334,12 +470,19 @@ public abstract class BatteryConsumer {
        return mPowerComponents.getConsumedPower(dimensions);
    }

    /**
     * Returns the amount of usage time  aggregated over the specified dimensions, in millis.
     */
    public long getUsageDurationMillis(@NonNull Dimensions dimensions) {
        return mPowerComponents.getUsageDurationMillis(dimensions);
    }

    /**
     * Returns keys for various power values attributed to the specified component
     * held by this BatteryUsageStats object.
     */
    public Key[] getKeys(@PowerComponent int componentId) {
        return mData.getKeys(componentId);
        return mData.layout.getKeys(componentId);
    }

    /**
@@ -347,14 +490,16 @@ public abstract class BatteryConsumer {
     * for all values of other dimensions such as process state.
     */
    public Key getKey(@PowerComponent int componentId) {
        return mData.getKey(componentId, PROCESS_STATE_UNSPECIFIED);
        return mData.layout.getKey(componentId, PROCESS_STATE_UNSPECIFIED, SCREEN_STATE_UNSPECIFIED,
                POWER_STATE_UNSPECIFIED);
    }

    /**
     * Returns the key for the power attributed to the specified component and process state.
     */
    public Key getKey(@PowerComponent int componentId, @ProcessState int processState) {
        return mData.getKey(componentId, processState);
        return mData.layout.getKey(componentId, processState, SCREEN_STATE_UNSPECIFIED,
                POWER_STATE_UNSPECIFIED);
    }

    /**
@@ -365,8 +510,8 @@ public abstract class BatteryConsumer {
     * @return Amount of consumed power in mAh.
     */
    public double getConsumedPower(@PowerComponent int componentId) {
        return mPowerComponents.getConsumedPower(
                mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
        return mPowerComponents.getConsumedPower(componentId, PROCESS_STATE_UNSPECIFIED,
                        SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED);
    }

    /**
@@ -388,7 +533,8 @@ public abstract class BatteryConsumer {
     */
    public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) {
        return mPowerComponents.getPowerModel(
                mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
                mData.layout.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED,
                        SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED));
    }

    /**
@@ -506,6 +652,20 @@ public abstract class BatteryConsumer {
        return sProcessStateNames[processState];
    }

    /**
     * Returns the human-readable name of the specified power state (on battery or not)
     */
    public static String powerStateToString(@PowerState int powerState) {
        return sPowerStateNames[powerState];
    }

    /**
     * Returns the human-readable name of the specified screen state (on or off/doze)
     */
    public static String screenStateToString(@ScreenState int screenState) {
        return sScreenStateNames[screenState];
    }

    /**
     * Prints the stats in a human-readable format.
     */
@@ -591,42 +751,11 @@ public abstract class BatteryConsumer {
            return new BatteryConsumerData(cursorWindow, cursorRow, layout);
        }

        public Key[] getKeys(int componentId) {
            return layout.keys[componentId];
        }

        Key getKeyOrThrow(int componentId, int processState) {
            Key key = getKey(componentId, processState);
            if (key == null) {
                if (processState == PROCESS_STATE_ANY) {
                    throw new IllegalArgumentException(
                            "Unsupported power component ID: " + componentId);
                } else {
                    throw new IllegalArgumentException(
                            "Unsupported power component ID: " + componentId
                                    + " process state: " + processState);
                }
            }
            return key;
        }

        Key getKey(int componentId, int processState) {
            if (componentId >= POWER_COMPONENT_COUNT) {
                return null;
            }

            if (processState == PROCESS_STATE_ANY) {
                // The 0-th key for each component corresponds to the roll-up,
                // across all dimensions. We might as well skip the iteration over the array.
                return layout.keys[componentId][0];
            } else {
                for (Key key : layout.keys[componentId]) {
                    if (key.processState == processState) {
                        return key;
                    }
                }
        boolean hasValue(int columnIndex) {
            if (mCursorRow == -1) {
                return false;
            }
            return null;
            return mCursorWindow.getType(mCursorRow, columnIndex) != Cursor.FIELD_TYPE_NULL;
        }

        void putInt(int columnIndex, int value) {
@@ -693,33 +822,68 @@ public abstract class BatteryConsumer {
        public final int customPowerComponentCount;
        public final boolean powerModelsIncluded;
        public final boolean processStateDataIncluded;
        public final Key[][] keys;
        public final boolean screenStateDataIncluded;
        public final boolean powerStateDataIncluded;
        public final Key[] keys;
        public final SparseArray<Key> indexedKeys;
        public final int totalConsumedPowerColumnIndex;
        public final int firstCustomConsumedPowerColumn;
        public final int firstCustomUsageDurationColumn;
        public final int columnCount;
        public final Key[][] processStateKeys;
        private Key[][] mPerComponentKeys;

        private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames,
                boolean powerModelsIncluded, boolean includeProcessStateData) {
                boolean powerModelsIncluded, boolean includeProcessStateData,
                boolean includeScreenState, boolean includePowerState) {
            this.customPowerComponentNames = customPowerComponentNames;
            this.customPowerComponentCount = customPowerComponentNames.length;
            this.powerModelsIncluded = powerModelsIncluded;
            this.processStateDataIncluded = includeProcessStateData;
            this.screenStateDataIncluded = includeScreenState;
            this.powerStateDataIncluded = includePowerState;

            int columnIndex = firstColumn;

            totalConsumedPowerColumnIndex = columnIndex++;

            keys = new Key[POWER_COMPONENT_COUNT][];

            ArrayList<Key> perComponentKeys = new ArrayList<>();
            ArrayList<Key> keyList = new ArrayList<>();
            for (int screenState = 0; screenState < SCREEN_STATE_COUNT; screenState++) {
                if (!includeScreenState && screenState != SCREEN_STATE_UNSPECIFIED) {
                    continue;
                }
                for (int powerState = 0; powerState < POWER_STATE_COUNT; powerState++) {
                    if (!includePowerState && powerState != POWER_STATE_UNSPECIFIED) {
                        continue;
                    }
                    for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) {
                perComponentKeys.clear();
                        columnIndex = addKeys(keyList, powerModelsIncluded, includeProcessStateData,
                                componentId, screenState, powerState, columnIndex);
                    }
                }
            }

            firstCustomConsumedPowerColumn = columnIndex;
            columnIndex += customPowerComponentCount;

            firstCustomUsageDurationColumn = columnIndex;
            columnIndex += customPowerComponentCount;

                // Declare the Key for the power component, ignoring other dimensions.
                perComponentKeys.add(
                        new Key(componentId, PROCESS_STATE_ANY,
            columnCount = columnIndex;

            keys = keyList.toArray(KEY_ARRAY);
            indexedKeys = new SparseArray<>(keys.length);
            for (int i = 0; i < keys.length; i++) {
                Key key = keys[i];
                int index = keyIndex(key.powerComponent, key.processState, key.screenState,
                        key.powerState);
                indexedKeys.put(index, key);
            }
        }

        private int addKeys(List<Key> keys, boolean powerModelsIncluded,
                boolean includeProcessStateData, int componentId,
                int screenState, int powerState, int columnIndex) {
            keys.add(new Key(componentId, PROCESS_STATE_ANY, screenState, powerState,
                    powerModelsIncluded
                            ? columnIndex++
                            : POWER_MODEL_NOT_INCLUDED,  // power model
@@ -729,13 +893,8 @@ public abstract class BatteryConsumer {

            // Declare Keys for all process states, if needed
            if (includeProcessStateData) {
                    boolean isSupported = false;
                    for (int id : SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE) {
                        if (id == componentId) {
                            isSupported = true;
                            break;
                        }
                    }
                boolean isSupported = SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE
                        .binarySearch(componentId) >= 0;
                if (isSupported) {
                    for (int processState = 0; processState < PROCESS_STATE_COUNT;
                            processState++) {
@@ -743,8 +902,7 @@ public abstract class BatteryConsumer {
                            continue;
                        }

                            perComponentKeys.add(
                                    new Key(componentId, processState,
                        keys.add(new Key(componentId, processState, screenState, powerState,
                                powerModelsIncluded
                                        ? columnIndex++
                                        : POWER_MODEL_NOT_INCLUDED, // power model
@@ -754,52 +912,68 @@ public abstract class BatteryConsumer {
                    }
                }
            }
            return columnIndex;
        }

                keys[componentId] = perComponentKeys.toArray(KEY_ARRAY);
        Key getKey(@PowerComponent int componentId, @ProcessState int processState,
                @ScreenState int screenState, @PowerState int powerState) {
            return indexedKeys.get(keyIndex(componentId, processState, screenState, powerState));
        }

            if (includeProcessStateData) {
                processStateKeys = new Key[BatteryConsumer.PROCESS_STATE_COUNT][];
                ArrayList<Key> perProcStateKeys = new ArrayList<>();
                for (int processState = 0; processState < PROCESS_STATE_COUNT; processState++) {
                    if (processState == PROCESS_STATE_UNSPECIFIED) {
                        continue;
        Key getKeyOrThrow(@PowerComponent int componentId, @ProcessState int processState,
                @ScreenState int screenState, @PowerState int powerState) {
            Key key = getKey(componentId, processState, screenState, powerState);
            if (key == null) {
                throw new IllegalArgumentException(
                        "Unsupported power component ID: " + Key.toString(componentId, processState,
                                screenState, powerState));
            }
            return key;
        }

                    perProcStateKeys.clear();
                    for (int i = 0; i < keys.length; i++) {
                        for (int j = 0; j < keys[i].length; j++) {
                            if (keys[i][j].processState == processState) {
                                perProcStateKeys.add(keys[i][j]);
        public Key[] getKeys(@PowerComponent int componentId) {
            synchronized (this) {
                if (mPerComponentKeys == null) {
                    mPerComponentKeys = new Key[BatteryConsumer.POWER_COMPONENT_COUNT][];
                }
                Key[] componentKeys = mPerComponentKeys[componentId];
                if (componentKeys == null) {
                    ArrayList<Key> out = new ArrayList<>();
                    for (Key key : keys) {
                        if (key.powerComponent == componentId) {
                            out.add(key);
                        }
                    }
                    processStateKeys[processState] = perProcStateKeys.toArray(KEY_ARRAY);
                    componentKeys = out.toArray(new Key[out.size()]);
                    mPerComponentKeys[componentId] = componentKeys;
                }
                return componentKeys;
            }
            } else {
                processStateKeys = null;
        }

            firstCustomConsumedPowerColumn = columnIndex;
            columnIndex += customPowerComponentCount;

            firstCustomUsageDurationColumn = columnIndex;
            columnIndex += customPowerComponentCount;

            columnCount = columnIndex;
        private int keyIndex(@PowerComponent int componentId, @ProcessState int processState,
                @ScreenState int screenState, @PowerState int powerState) {
            // [CCCCCCPPPSSBB]
            // C - component ID
            // P - process state
            // S - screen state
            // B - power state
            return componentId << 7 | processState << 4 | screenState << 2 | powerState;
        }
    }

    static BatteryConsumerDataLayout createBatteryConsumerDataLayout(
            String[] customPowerComponentNames, boolean includePowerModels,
            boolean includeProcessStateData) {
            boolean includeProcessStateData, boolean includeScreenStateData,
            boolean includePowerStateData) {
        int columnCount = BatteryConsumer.COLUMN_COUNT;
        columnCount = Math.max(columnCount, AggregateBatteryConsumer.COLUMN_COUNT);
        columnCount = Math.max(columnCount, UidBatteryConsumer.COLUMN_COUNT);
        columnCount = Math.max(columnCount, UserBatteryConsumer.COLUMN_COUNT);

        return new BatteryConsumerDataLayout(columnCount, customPowerComponentNames,
                includePowerModels, includeProcessStateData);
                includePowerModels, includeProcessStateData, includeScreenStateData,
                includePowerStateData);
    }

    protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
@@ -816,12 +990,19 @@ public abstract class BatteryConsumer {

        @Nullable
        public Key[] getKeys(@PowerComponent int componentId) {
            return mData.getKeys(componentId);
            return mData.layout.getKeys(componentId);
        }

        @Nullable
        public Key getKey(@PowerComponent int componentId, @ProcessState int processState) {
            return mData.getKey(componentId, processState);
            return mData.layout.getKey(componentId, processState, SCREEN_STATE_UNSPECIFIED,
                    POWER_STATE_UNSPECIFIED);
        }

        @Nullable
        public Key getKey(@PowerComponent int componentId, @ProcessState int processState,
                @ScreenState int screenState, @PowerState int powerState) {
            return mData.layout.getKey(componentId, processState, screenState, powerState);
        }

        /**
Loading