/* * Copyright (C) 2020 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 android.os; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.proto.ProtoOutputStream; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Interface for objects containing battery attribution data. * * @hide */ public abstract class BatteryConsumer { /** * Power usage component, describing the particular part of the system * responsible for power drain. * * @hide */ @IntDef(prefix = {"POWER_COMPONENT_"}, value = { POWER_COMPONENT_SCREEN, POWER_COMPONENT_CPU, POWER_COMPONENT_BLUETOOTH, POWER_COMPONENT_CAMERA, POWER_COMPONENT_AUDIO, POWER_COMPONENT_VIDEO, POWER_COMPONENT_FLASHLIGHT, POWER_COMPONENT_MOBILE_RADIO, POWER_COMPONENT_SYSTEM_SERVICES, POWER_COMPONENT_SENSORS, POWER_COMPONENT_GNSS, POWER_COMPONENT_WIFI, POWER_COMPONENT_WAKELOCK, POWER_COMPONENT_MEMORY, POWER_COMPONENT_PHONE, POWER_COMPONENT_IDLE, POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, }) @Retention(RetentionPolicy.SOURCE) public static @interface PowerComponent { } 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 public static final int POWER_COMPONENT_CAMERA = OsProtoEnums.POWER_COMPONENT_CAMERA; // 3 public static final int POWER_COMPONENT_AUDIO = OsProtoEnums.POWER_COMPONENT_AUDIO; // 4 public static final int POWER_COMPONENT_VIDEO = OsProtoEnums.POWER_COMPONENT_VIDEO; // 5 public static final int POWER_COMPONENT_FLASHLIGHT = OsProtoEnums.POWER_COMPONENT_FLASHLIGHT; // 6 public static final int POWER_COMPONENT_SYSTEM_SERVICES = OsProtoEnums.POWER_COMPONENT_SYSTEM_SERVICES; // 7 public static final int POWER_COMPONENT_MOBILE_RADIO = OsProtoEnums.POWER_COMPONENT_MOBILE_RADIO; // 8 public static final int POWER_COMPONENT_SENSORS = OsProtoEnums.POWER_COMPONENT_SENSORS; // 9 public static final int POWER_COMPONENT_GNSS = OsProtoEnums.POWER_COMPONENT_GNSS; // 10 public static final int POWER_COMPONENT_WIFI = OsProtoEnums.POWER_COMPONENT_WIFI; // 11 public static final int POWER_COMPONENT_WAKELOCK = OsProtoEnums.POWER_COMPONENT_WAKELOCK; // 12 public static final int POWER_COMPONENT_MEMORY = OsProtoEnums.POWER_COMPONENT_MEMORY; // 13 public static final int POWER_COMPONENT_PHONE = OsProtoEnums.POWER_COMPONENT_PHONE; // 14 public static final int POWER_COMPONENT_AMBIENT_DISPLAY = OsProtoEnums.POWER_COMPONENT_AMBIENT_DISPLAY; // 15 public static final int POWER_COMPONENT_IDLE = OsProtoEnums.POWER_COMPONENT_IDLE; // 16 // Power that is re-attributed to other battery consumers. For example, for System Server // this represents the power attributed to apps requesting system services. // The value should be negative or zero. public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = OsProtoEnums.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS; // 17 public static final int POWER_COMPONENT_COUNT = 18; public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; private static final String[] sPowerComponentNames = new String[POWER_COMPONENT_COUNT]; static { // Assign individually to avoid future mismatch sPowerComponentNames[POWER_COMPONENT_SCREEN] = "screen"; sPowerComponentNames[POWER_COMPONENT_CPU] = "cpu"; sPowerComponentNames[POWER_COMPONENT_BLUETOOTH] = "bluetooth"; sPowerComponentNames[POWER_COMPONENT_CAMERA] = "camera"; sPowerComponentNames[POWER_COMPONENT_AUDIO] = "audio"; sPowerComponentNames[POWER_COMPONENT_VIDEO] = "video"; sPowerComponentNames[POWER_COMPONENT_FLASHLIGHT] = "flashlight"; sPowerComponentNames[POWER_COMPONENT_SYSTEM_SERVICES] = "system_services"; sPowerComponentNames[POWER_COMPONENT_MOBILE_RADIO] = "mobile_radio"; sPowerComponentNames[POWER_COMPONENT_SENSORS] = "sensors"; sPowerComponentNames[POWER_COMPONENT_GNSS] = "gnss"; sPowerComponentNames[POWER_COMPONENT_WIFI] = "wifi"; sPowerComponentNames[POWER_COMPONENT_WAKELOCK] = "wakelock"; sPowerComponentNames[POWER_COMPONENT_MEMORY] = "memory"; sPowerComponentNames[POWER_COMPONENT_PHONE] = "phone"; sPowerComponentNames[POWER_COMPONENT_AMBIENT_DISPLAY] = "ambient_display"; sPowerComponentNames[POWER_COMPONENT_IDLE] = "idle"; sPowerComponentNames[POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS] = "reattributed"; } /** * Identifiers of models used for power estimation. * * @hide */ @IntDef(prefix = {"POWER_MODEL_"}, value = { POWER_MODEL_UNDEFINED, POWER_MODEL_POWER_PROFILE, POWER_MODEL_MEASURED_ENERGY, }) @Retention(RetentionPolicy.SOURCE) public @interface PowerModel { } /** * Unspecified power model. */ public static final int POWER_MODEL_UNDEFINED = 0; /** * Power model that is based on average consumption rates that hardware components * consume in various states. */ public static final int POWER_MODEL_POWER_PROFILE = 1; /** * Power model that is based on energy consumption measured by on-device power monitors. */ public static final int POWER_MODEL_MEASURED_ENERGY = 2; protected final PowerComponents mPowerComponents; protected BatteryConsumer(@NonNull PowerComponents powerComponents) { mPowerComponents = powerComponents; } /** * Total power consumed by this consumer, in mAh. */ public double getConsumedPower() { return mPowerComponents.getConsumedPower(); } /** * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. * * @param componentId The ID of the power component, e.g. * {@link BatteryConsumer#POWER_COMPONENT_CPU}. * @return Amount of consumed power in mAh. */ public double getConsumedPower(@PowerComponent int componentId) { return mPowerComponents.getConsumedPower(componentId); } /** * Returns the ID of the model that was used for power estimation. * * @param componentId The ID of the power component, e.g. * {@link BatteryConsumer#POWER_COMPONENT_CPU}. */ public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) { return mPowerComponents.getPowerModel(componentId); } /** * Returns the amount of drain attributed to the specified custom drain type. * * @param componentId The ID of the custom power component. * @return Amount of consumed power in mAh. */ public double getConsumedPowerForCustomComponent(int componentId) { return mPowerComponents.getConsumedPowerForCustomComponent(componentId); } public int getCustomPowerComponentCount() { return mPowerComponents.getCustomPowerComponentCount(); } void setCustomPowerComponentNames(String[] customPowerComponentNames) { mPowerComponents.setCustomPowerComponentNames(customPowerComponentNames); } /** * Returns the name of the specified power component. * * @param componentId The ID of the custom power component. */ public String getCustomPowerComponentName(int componentId) { return mPowerComponents.getCustomPowerComponentName(componentId); } /** * Returns the amount of time since BatteryStats reset used by the specified component, e.g. * CPU, WiFi etc. * * @param componentId The ID of the power component, e.g. * {@link UidBatteryConsumer#POWER_COMPONENT_CPU}. * @return Amount of time in milliseconds. */ public long getUsageDurationMillis(@PowerComponent int componentId) { return mPowerComponents.getUsageDurationMillis(componentId); } /** * Returns the amount of usage time attributed to the specified custom component * since BatteryStats reset. * * @param componentId The ID of the custom power component. * @return Amount of time in milliseconds. */ public long getUsageDurationForCustomComponentMillis(int componentId) { return mPowerComponents.getUsageDurationForCustomComponentMillis(componentId); } protected void writeToParcel(Parcel dest, int flags) { mPowerComponents.writeToParcel(dest, flags); } /** * Returns the name of the specified component. Intended for logging and debugging. */ public static String powerComponentIdToString(@BatteryConsumer.PowerComponent int componentId) { return sPowerComponentNames[componentId]; } /** * Returns the name of the specified power model. Intended for logging and debugging. */ public static String powerModelToString(@BatteryConsumer.PowerModel int powerModel) { switch (powerModel) { case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: return "measured energy"; case BatteryConsumer.POWER_MODEL_POWER_PROFILE: return "power profile"; default: return ""; } } /** * Prints the stats in a human-readable format. */ public void dump(PrintWriter pw) { dump(pw, true); } /** * Prints the stats in a human-readable format. * * @param skipEmptyComponents if true, omit any power components with a zero amount. */ public abstract void dump(PrintWriter pw, boolean skipEmptyComponents); /** Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto. */ boolean hasStatsProtoData() { return writeStatsProtoImpl(null, /* Irrelevant fieldId: */ 0); } /** Writes the atoms.proto BATTERY_CONSUMER_DATA for this BatteryConsumer to the given proto. */ void writeStatsProto(@NonNull ProtoOutputStream proto, long fieldId) { writeStatsProtoImpl(proto, fieldId); } /** * Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto, * and writes it to the given proto if it is non-null. */ private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto, long fieldId) { final long totalConsumedPowerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower()); if (totalConsumedPowerDeciCoulombs == 0) { // NOTE: Strictly speaking we should also check !mPowerComponents.hasStatsProtoData(). // However, that call is a bit expensive (a for loop). And the only way that // totalConsumedPower can be 0 while mPowerComponents.hasStatsProtoData() is true is // if POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS (which is the only negative // allowed) happens to exactly equal the sum of all other components, which // can't really happen in practice. // So we'll just adopt the rule "if total==0, don't write any details". // If negative values are used for other things in the future, this can be revisited. return false; } if (proto == null) { // We're just asked whether there is data, not to actually write it. And there is. return true; } final long token = proto.start(fieldId); proto.write( BatteryUsageStatsAtomsProto.BatteryConsumerData.TOTAL_CONSUMED_POWER_DECI_COULOMBS, totalConsumedPowerDeciCoulombs); mPowerComponents.writeStatsProto(proto); proto.end(token); return true; } /** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */ static long convertMahToDeciCoulombs(double powerMah) { return (long) (powerMah * (10 * 3600 / 1000) + 0.5); } protected abstract static class BaseBuilder> { final PowerComponents.Builder mPowerComponentsBuilder; public BaseBuilder(@NonNull String[] customPowerComponentNames, boolean includePowerModels) { mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentNames, includePowerModels); } /** * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. * * @param componentId The ID of the power component, e.g. * {@link BatteryConsumer#POWER_COMPONENT_CPU}. * @param componentPower Amount of consumed power in mAh. */ @NonNull public T setConsumedPower(@PowerComponent int componentId, double componentPower) { return setConsumedPower(componentId, componentPower, POWER_MODEL_POWER_PROFILE); } /** * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. * * @param componentId The ID of the power component, e.g. * {@link BatteryConsumer#POWER_COMPONENT_CPU}. * @param componentPower Amount of consumed power in mAh. */ @SuppressWarnings("unchecked") @NonNull public T setConsumedPower(@PowerComponent int componentId, double componentPower, @PowerModel int powerModel) { mPowerComponentsBuilder.setConsumedPower(componentId, componentPower, powerModel); return (T) this; } /** * Sets the amount of drain attributed to the specified custom drain type. * * @param componentId The ID of the custom power component. * @param componentPower Amount of consumed power in mAh. */ @SuppressWarnings("unchecked") @NonNull public T setConsumedPowerForCustomComponent(int componentId, double componentPower) { mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower); return (T) this; } /** * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc. * * @param componentId The ID of the power component, e.g. * {@link UidBatteryConsumer#POWER_COMPONENT_CPU}. * @param componentUsageTimeMillis Amount of time in microseconds. */ @SuppressWarnings("unchecked") @NonNull public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId, long componentUsageTimeMillis) { mPowerComponentsBuilder.setUsageDurationMillis(componentId, componentUsageTimeMillis); return (T) this; } /** * Sets the amount of time used by the specified custom component. * * @param componentId The ID of the custom power component. * @param componentUsageTimeMillis Amount of time in microseconds. */ @SuppressWarnings("unchecked") @NonNull public T setUsageDurationForCustomComponentMillis(int componentId, long componentUsageTimeMillis) { mPowerComponentsBuilder.setUsageDurationForCustomComponentMillis(componentId, componentUsageTimeMillis); return (T) this; } /** * Returns the total power accumulated by this builder so far. It may change * by the time the {@code build()} method is called. */ public double getTotalPower() { return mPowerComponentsBuilder.getTotalPower(); } } }