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

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

Merge "Add per-proc-state power components"

parents b95505f1 2198961a
Loading
Loading
Loading
Loading
+334 −27
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;

/**
 * Interface for objects containing battery attribution data.
@@ -43,6 +44,7 @@ public abstract class BatteryConsumer {
     * @hide
     */
    @IntDef(prefix = {"POWER_COMPONENT_"}, value = {
            POWER_COMPONENT_ANY,
            POWER_COMPONENT_SCREEN,
            POWER_COMPONENT_CPU,
            POWER_COMPONENT_BLUETOOTH,
@@ -65,6 +67,7 @@ public abstract class BatteryConsumer {
    public static @interface PowerComponent {
    }

    public static final int POWER_COMPONENT_ANY = -1;
    public static final int POWER_COMPONENT_SCREEN = OsProtoEnums.POWER_COMPONENT_SCREEN; // 0
    public static final int POWER_COMPONENT_CPU = OsProtoEnums.POWER_COMPONENT_CPU; // 1
    public static final int POWER_COMPONENT_BLUETOOTH = OsProtoEnums.POWER_COMPONENT_BLUETOOTH; // 2
@@ -151,9 +154,145 @@ public abstract class BatteryConsumer {
     */
    public static final int POWER_MODEL_MEASURED_ENERGY = 2;

    /**
     * Identifiers of consumed power aggregations.
     *
     * @hide
     */
    @IntDef(prefix = {"PROCESS_STATE_"}, value = {
            PROCESS_STATE_ANY,
            PROCESS_STATE_FOREGROUND,
            PROCESS_STATE_BACKGROUND,
            PROCESS_STATE_FOREGROUND_SERVICE,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ProcessState {
    }

    public static final int PROCESS_STATE_ANY = 0;
    public static final int PROCESS_STATE_FOREGROUND = 1;
    public static final int PROCESS_STATE_BACKGROUND = 2;
    public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;

    static final int PROCESS_STATE_COUNT = 4;

    private static final String[] sProcessStateNames = new String[PROCESS_STATE_COUNT];

    static {
        // Assign individually to avoid future mismatch
        sProcessStateNames[PROCESS_STATE_ANY] = "any";
        sProcessStateNames[PROCESS_STATE_FOREGROUND] = "fg";
        sProcessStateNames[PROCESS_STATE_BACKGROUND] = "bg";
        sProcessStateNames[PROCESS_STATE_FOREGROUND_SERVICE] = "fgs";
    }

    private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
            POWER_COMPONENT_CPU,
    };

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

    /**
     * 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 Dimensions(int powerComponent, int processState) {
            this.powerComponent = powerComponent;
            this.processState = processState;
        }

        @Override
        public String toString() {
            boolean dimensionSpecified = false;
            StringBuilder sb = new StringBuilder();
            if (powerComponent != POWER_COMPONENT_ANY) {
                sb.append("powerComponent=").append(sPowerComponentNames[powerComponent]);
                dimensionSpecified = true;
            }
            if (processState != PROCESS_STATE_ANY) {
                if (dimensionSpecified) {
                    sb.append(", ");
                }
                sb.append("processState=").append(sProcessStateNames[processState]);
                dimensionSpecified = true;
            }
            if (!dimensionSpecified) {
                sb.append("any components and process states");
            }
            return sb.toString();
        }
    }

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

    /**
     * Identifies power attribution dimensions that are captured by an data element of
     * a BatteryConsumer. These Keys are used to access those values and to set them using
     * Builders.  See for example {@link #getConsumedPower(Key)}.
     *
     * Keys cannot be allocated by the client - they can only be obtained by calling
     * {@link #getKeys} or {@link #getKey}.  All BatteryConsumers that are part of the
     * same BatteryUsageStats share the same set of keys, therefore it is safe to obtain
     * the keys from one BatteryConsumer and apply them to other BatteryConsumers
     * in the same BatteryUsageStats.
     */
    public static final class Key {
        public final @PowerComponent int powerComponent;
        public final @ProcessState int processState;

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

        private Key(int powerComponent, int processState, int powerModelColumnIndex,
                int powerColumnIndex, int durationColumnIndex) {
            this.powerComponent = powerComponent;
            this.processState = processState;

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

        @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;
        }

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

        /**
         * Returns a string suitable for use in dumpsys.
         */
        public String toShortString() {
            if (mShortString == null) {
                StringBuilder sb = new StringBuilder();
                sb.append(powerComponentIdToString(powerComponent));
                if (processState != PROCESS_STATE_ANY) {
                    sb.append(':');
                    sb.append(processStateToString(processState));
                }
                mShortString = sb.toString();
            }
            return mShortString;
        }
    }

    protected final BatteryConsumerData mData;
    protected final PowerComponents mPowerComponents;

@@ -171,7 +310,37 @@ public abstract class BatteryConsumer {
     * Total power consumed by this consumer, in mAh.
     */
    public double getConsumedPower() {
        return mPowerComponents.getConsumedPower();
        return mPowerComponents.getConsumedPower(UNSPECIFIED_DIMENSIONS);
    }

    /**
     * Returns power consumed aggregated over the specified dimensions, in mAh.
     */
    public double getConsumedPower(Dimensions dimensions) {
        return mPowerComponents.getConsumedPower(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);
    }

    /**
     * Returns the key for the power attributed to the specified component,
     * for all values of other dimensions such as process state.
     */
    public Key getKey(@PowerComponent int componentId) {
        return mData.getKey(componentId, PROCESS_STATE_ANY);
    }

    /**
     * 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);
    }

    /**
@@ -182,7 +351,19 @@ public abstract class BatteryConsumer {
     * @return Amount of consumed power in mAh.
     */
    public double getConsumedPower(@PowerComponent int componentId) {
        return mPowerComponents.getConsumedPower(componentId);
        return mPowerComponents.getConsumedPower(
                mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
    }

    /**
     * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
     *
     * @param key The key of the power component, obtained by calling {@link #getKey} or
     *            {@link #getKeys} method.
     * @return Amount of consumed power in mAh.
     */
    public double getConsumedPower(@NonNull Key key) {
        return mPowerComponents.getConsumedPower(key);
    }

    /**
@@ -192,7 +373,18 @@ public abstract class BatteryConsumer {
     *                    {@link BatteryConsumer#POWER_COMPONENT_CPU}.
     */
    public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) {
        return mPowerComponents.getPowerModel(componentId);
        return mPowerComponents.getPowerModel(
                mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
    }

    /**
     * Returns the ID of the model that was used for power estimation.
     *
     * @param key The key of the power component, obtained by calling {@link #getKey} or
     *            {@link #getKeys} method.
     */
    public @PowerModel int getPowerModel(@NonNull BatteryConsumer.Key key) {
        return mPowerComponents.getPowerModel(key);
    }

    /**
@@ -227,7 +419,20 @@ public abstract class BatteryConsumer {
     * @return Amount of time in milliseconds.
     */
    public long getUsageDurationMillis(@PowerComponent int componentId) {
        return mPowerComponents.getUsageDurationMillis(componentId);
        return mPowerComponents.getUsageDurationMillis(getKey(componentId));
    }

    /**
     * Returns the amount of time since BatteryStats reset used by the specified component, e.g.
     * CPU, WiFi etc.
     *
     *
     * @param key The key of the power component, obtained by calling {@link #getKey} or
     *            {@link #getKeys} method.
     * @return Amount of time in milliseconds.
     */
    public long getUsageDurationMillis(@NonNull Key key) {
        return mPowerComponents.getUsageDurationMillis(key);
    }

    /**
@@ -245,6 +450,9 @@ public abstract class BatteryConsumer {
     * Returns the name of the specified component.  Intended for logging and debugging.
     */
    public static String powerComponentIdToString(@BatteryConsumer.PowerComponent int componentId) {
        if (componentId == POWER_COMPONENT_ANY) {
            return "all";
        }
        return sPowerComponentNames[componentId];
    }

@@ -262,6 +470,13 @@ public abstract class BatteryConsumer {
        }
    }

    /**
     * Returns the name of the specified process state.  Intended for logging and debugging.
     */
    public static String processStateToString(@BatteryConsumer.ProcessState int processState) {
        return sProcessStateNames[processState];
    }

    /**
     * Prints the stats in a human-readable format.
     */
@@ -347,6 +562,44 @@ 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;
                    }
                }
            }
            return null;
        }

        void putInt(int columnIndex, int value) {
            if (mCursorRow == -1) {
                return;
@@ -405,59 +658,91 @@ public abstract class BatteryConsumer {
    }

    static class BatteryConsumerDataLayout {
        private static final Key[] KEY_ARRAY = new Key[0];
        public final String[] customPowerComponentNames;
        public final int customPowerComponentCount;
        public final boolean powerModelsIncluded;

        public final int consumedPowerColumn;
        public final int firstConsumedPowerColumn;
        public final boolean processStateDataIncluded;
        public final Key[][] keys;
        public final int totalConsumedPowerColumnIndex;
        public final int firstCustomConsumedPowerColumn;
        public final int firstUsageDurationColumn;
        public final int firstCustomUsageDurationColumn;
        public final int firstPowerModelColumn;
        public final int columnCount;

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

            int columnIndex = firstColumn;
            consumedPowerColumn = columnIndex++;

            firstConsumedPowerColumn = columnIndex;
            columnIndex += BatteryConsumer.POWER_COMPONENT_COUNT;
            totalConsumedPowerColumnIndex = columnIndex++;

            keys = new Key[POWER_COMPONENT_COUNT][];

            ArrayList<Key> perComponentKeys = new ArrayList<>();
            for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) {
                perComponentKeys.clear();

                // Declare the Key for the power component, ignoring other dimensions.
                perComponentKeys.add(
                        new Key(componentId, PROCESS_STATE_ANY,
                                powerModelsIncluded ? columnIndex++ : -1,  // power model
                                columnIndex++,      // power
                                columnIndex++       // usage duration
                        ));

                // 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;
                        }
                    }
                    if (isSupported) {
                        for (int processState = 0; processState < PROCESS_STATE_COUNT;
                                processState++) {
                            if (processState == PROCESS_STATE_ANY) {
                                continue;
                            }

                            perComponentKeys.add(
                                    new Key(componentId, processState,
                                            powerModelsIncluded ? columnIndex++ : -1, // power model
                                            columnIndex++,      // power
                                            columnIndex++       // usage duration
                                    ));
                        }
                    }
                }

                keys[componentId] = perComponentKeys.toArray(KEY_ARRAY);
            }

            firstCustomConsumedPowerColumn = columnIndex;
            columnIndex += customPowerComponentCount;

            firstUsageDurationColumn = columnIndex;
            columnIndex += BatteryConsumer.POWER_COMPONENT_COUNT;

            firstCustomUsageDurationColumn = columnIndex;
            columnIndex += customPowerComponentCount;

            if (powerModelsIncluded) {
                firstPowerModelColumn = columnIndex;
                columnIndex += BatteryConsumer.POWER_COMPONENT_COUNT;
            } else {
                firstPowerModelColumn = -1;
            }

            columnCount = columnIndex;
        }
    }

    static BatteryConsumerDataLayout createBatteryConsumerDataLayout(
            String[] customPowerComponentNames, boolean includePowerModels) {
            String[] customPowerComponentNames, boolean includePowerModels,
            boolean includeProcessStateData) {
        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);
                includePowerModels, includeProcessStateData);
    }

    protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
@@ -471,6 +756,11 @@ public abstract class BatteryConsumer {
            mPowerComponentsBuilder = new PowerComponents.Builder(data);
        }

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

        /**
         * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
         *
@@ -494,7 +784,15 @@ public abstract class BatteryConsumer {
        @NonNull
        public T setConsumedPower(@PowerComponent int componentId, double componentPower,
                @PowerModel int powerModel) {
            mPowerComponentsBuilder.setConsumedPower(componentId, componentPower, powerModel);
            mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_ANY),
                    componentPower, powerModel);
            return (T) this;
        }

        @SuppressWarnings("unchecked")
        @NonNull
        public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
            mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel);
            return (T) this;
        }

@@ -522,7 +820,16 @@ public abstract class BatteryConsumer {
        @NonNull
        public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId,
                long componentUsageTimeMillis) {
            mPowerComponentsBuilder.setUsageDurationMillis(componentId, componentUsageTimeMillis);
            mPowerComponentsBuilder.setUsageDurationMillis(getKey(componentId, PROCESS_STATE_ANY),
                    componentUsageTimeMillis);
            return (T) this;
        }


        @SuppressWarnings("unchecked")
        @NonNull
        public T setUsageDurationMillis(Key key, long componentUsageTimeMillis) {
            mPowerComponentsBuilder.setUsageDurationMillis(key, componentUsageTimeMillis);
            return (T) this;
        }

+6 −0
Original line number Diff line number Diff line
@@ -653,6 +653,11 @@ public abstract class BatteryStats implements Parcelable {
        }
    }

    /**
     * Returns true if battery consumption is tracked on a per-process-state basis.
     */
    public abstract boolean isProcessStateDataAvailable();

    /**
     * The statistics associated with a particular uid.
     */
@@ -5406,6 +5411,7 @@ public abstract class BatteryStats implements Parcelable {
                new BatteryUsageStatsQuery.Builder()
                        .setMaxStatsAgeMs(0)
                        .includePowerModels()
                        .includeProcessStateData()
                        .build());
        stats.dump(pw, prefix);

+51 −17

File changed.

Preview size limit exceeded, changes collapsed.

+16 −3
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
    @IntDef(flag = true, prefix = { "FLAG_BATTERY_USAGE_STATS_" }, value = {
            FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL,
            FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY,
            FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface BatteryUsageStatsFlags {}
@@ -52,19 +53,21 @@ public final class BatteryUsageStatsQuery implements Parcelable {
     *
     * @hide
     */
    public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1;
    public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 0x0001;

    /**
     * Indicates that battery history should be included in the BatteryUsageStats.
     * @hide
     */
    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 2;
    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 0x0002;

    /**
     * Indicates that identifiers of power models used for computations of power
     * consumption should be included in the BatteryUsageStats.
     */
    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS = 4;
    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS = 0x0004;

    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA = 0x0008;

    private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000;

@@ -208,6 +211,16 @@ public final class BatteryUsageStatsQuery implements Parcelable {
            return this;
        }

        /**
         * Requests that per-process state data be included in the BatteryUsageStats, if
         * available. Check {@link BatteryUsageStats#isProcessStateDataIncluded()} on the result
         * to see if the data is available.
         */
        public Builder includeProcessStateData() {
            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA;
            return this;
        }

        /**
         * Requests to return modeled battery usage stats only, even if on-device
         * power monitoring data is available.
+178 −127

File changed.

Preview size limit exceeded, changes collapsed.

Loading