935 lines
36 KiB
Java
935 lines
36 KiB
Java
/*
|
|
* 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.database.CursorWindow;
|
|
import android.util.Slog;
|
|
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.
|
|
*
|
|
* @hide
|
|
*/
|
|
@android.ravenwood.annotation.RavenwoodKeepWholeClass
|
|
public abstract class BatteryConsumer {
|
|
|
|
private static final String TAG = "BatteryConsumer";
|
|
|
|
/**
|
|
* Power usage component, describing the particular part of the system
|
|
* responsible for power drain.
|
|
*
|
|
* @hide
|
|
*/
|
|
@IntDef(prefix = {"POWER_COMPONENT_"}, value = {
|
|
POWER_COMPONENT_ANY,
|
|
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_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
|
|
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_ENERGY_CONSUMPTION,
|
|
})
|
|
@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 stats provided by PowerStats HAL.
|
|
*/
|
|
public static final int POWER_MODEL_ENERGY_CONSUMPTION = 2;
|
|
|
|
/**
|
|
* Identifiers of consumed power aggregations.
|
|
*
|
|
* @hide
|
|
*/
|
|
@IntDef(prefix = {"PROCESS_STATE_"}, value = {
|
|
PROCESS_STATE_ANY,
|
|
PROCESS_STATE_UNSPECIFIED,
|
|
PROCESS_STATE_FOREGROUND,
|
|
PROCESS_STATE_BACKGROUND,
|
|
PROCESS_STATE_FOREGROUND_SERVICE,
|
|
PROCESS_STATE_CACHED,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface ProcessState {
|
|
}
|
|
|
|
public static final int PROCESS_STATE_UNSPECIFIED = 0;
|
|
public static final int PROCESS_STATE_ANY = PROCESS_STATE_UNSPECIFIED;
|
|
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;
|
|
public static final int PROCESS_STATE_CACHED = 4;
|
|
|
|
public static final int PROCESS_STATE_COUNT = 5;
|
|
|
|
private static final String[] sProcessStateNames = new String[PROCESS_STATE_COUNT];
|
|
|
|
static {
|
|
// Assign individually to avoid future mismatch
|
|
sProcessStateNames[PROCESS_STATE_UNSPECIFIED] = "unspecified";
|
|
sProcessStateNames[PROCESS_STATE_FOREGROUND] = "fg";
|
|
sProcessStateNames[PROCESS_STATE_BACKGROUND] = "bg";
|
|
sProcessStateNames[PROCESS_STATE_FOREGROUND_SERVICE] = "fgs";
|
|
sProcessStateNames[PROCESS_STATE_CACHED] = "cached";
|
|
}
|
|
|
|
private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
|
|
POWER_COMPONENT_CPU,
|
|
POWER_COMPONENT_MOBILE_RADIO,
|
|
POWER_COMPONENT_WIFI,
|
|
POWER_COMPONENT_BLUETOOTH,
|
|
};
|
|
|
|
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_UNSPECIFIED) {
|
|
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 a 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_UNSPECIFIED) {
|
|
sb.append(':');
|
|
sb.append(processStateToString(processState));
|
|
}
|
|
mShortString = sb.toString();
|
|
}
|
|
return mShortString;
|
|
}
|
|
}
|
|
|
|
protected final BatteryConsumerData mData;
|
|
protected final PowerComponents mPowerComponents;
|
|
|
|
protected BatteryConsumer(BatteryConsumerData data, @NonNull PowerComponents powerComponents) {
|
|
mData = data;
|
|
mPowerComponents = powerComponents;
|
|
}
|
|
|
|
public BatteryConsumer(BatteryConsumerData data) {
|
|
mData = data;
|
|
mPowerComponents = new PowerComponents(data);
|
|
}
|
|
|
|
/**
|
|
* Total power consumed by this consumer, in mAh.
|
|
*/
|
|
public double 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_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);
|
|
}
|
|
|
|
/**
|
|
* 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(
|
|
mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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(
|
|
mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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 mData.layout.customPowerComponentCount;
|
|
}
|
|
|
|
/**
|
|
* 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(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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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];
|
|
}
|
|
|
|
/**
|
|
* 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_ENERGY_CONSUMPTION:
|
|
return "energy consumption";
|
|
case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
|
|
return "power profile";
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the equivalent PowerModel enum for the specified power model.
|
|
* {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel}
|
|
*/
|
|
public static int powerModelToProtoEnum(@BatteryConsumer.PowerModel int powerModel) {
|
|
switch (powerModel) {
|
|
case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
|
|
return BatteryUsageStatsAtomsProto.PowerComponentModel.MEASURED_ENERGY;
|
|
case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
|
|
return BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_PROFILE;
|
|
default:
|
|
return BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
static class BatteryConsumerData {
|
|
private final CursorWindow mCursorWindow;
|
|
private final int mCursorRow;
|
|
public final BatteryConsumerDataLayout layout;
|
|
|
|
BatteryConsumerData(CursorWindow cursorWindow, int cursorRow,
|
|
BatteryConsumerDataLayout layout) {
|
|
mCursorWindow = cursorWindow;
|
|
mCursorRow = cursorRow;
|
|
this.layout = layout;
|
|
}
|
|
|
|
@Nullable
|
|
static BatteryConsumerData create(CursorWindow cursorWindow,
|
|
BatteryConsumerDataLayout layout) {
|
|
int cursorRow = cursorWindow.getNumRows();
|
|
if (!cursorWindow.allocRow()) {
|
|
Slog.e(TAG, "Cannot allocate BatteryConsumerData: too many UIDs: " + cursorRow);
|
|
cursorRow = -1;
|
|
}
|
|
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;
|
|
}
|
|
mCursorWindow.putLong(value, mCursorRow, columnIndex);
|
|
}
|
|
|
|
int getInt(int columnIndex) {
|
|
if (mCursorRow == -1) {
|
|
return 0;
|
|
}
|
|
return mCursorWindow.getInt(mCursorRow, columnIndex);
|
|
}
|
|
|
|
void putDouble(int columnIndex, double value) {
|
|
if (mCursorRow == -1) {
|
|
return;
|
|
}
|
|
mCursorWindow.putDouble(value, mCursorRow, columnIndex);
|
|
}
|
|
|
|
double getDouble(int columnIndex) {
|
|
if (mCursorRow == -1) {
|
|
return 0;
|
|
}
|
|
return mCursorWindow.getDouble(mCursorRow, columnIndex);
|
|
}
|
|
|
|
void putLong(int columnIndex, long value) {
|
|
if (mCursorRow == -1) {
|
|
return;
|
|
}
|
|
mCursorWindow.putLong(value, mCursorRow, columnIndex);
|
|
}
|
|
|
|
long getLong(int columnIndex) {
|
|
if (mCursorRow == -1) {
|
|
return 0;
|
|
}
|
|
return mCursorWindow.getLong(mCursorRow, columnIndex);
|
|
}
|
|
|
|
void putString(int columnIndex, String value) {
|
|
if (mCursorRow == -1) {
|
|
return;
|
|
}
|
|
mCursorWindow.putString(value, mCursorRow, columnIndex);
|
|
}
|
|
|
|
String getString(int columnIndex) {
|
|
if (mCursorRow == -1) {
|
|
return null;
|
|
}
|
|
return mCursorWindow.getString(mCursorRow, columnIndex);
|
|
}
|
|
}
|
|
|
|
static class BatteryConsumerDataLayout {
|
|
private static final Key[] KEY_ARRAY = new Key[0];
|
|
public static final int POWER_MODEL_NOT_INCLUDED = -1;
|
|
public final String[] customPowerComponentNames;
|
|
public final int customPowerComponentCount;
|
|
public final boolean powerModelsIncluded;
|
|
public final boolean processStateDataIncluded;
|
|
public final Key[][] keys;
|
|
public final int totalConsumedPowerColumnIndex;
|
|
public final int firstCustomConsumedPowerColumn;
|
|
public final int firstCustomUsageDurationColumn;
|
|
public final int columnCount;
|
|
public final Key[][] processStateKeys;
|
|
|
|
private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames,
|
|
boolean powerModelsIncluded, boolean includeProcessStateData) {
|
|
this.customPowerComponentNames = customPowerComponentNames;
|
|
this.customPowerComponentCount = customPowerComponentNames.length;
|
|
this.powerModelsIncluded = powerModelsIncluded;
|
|
this.processStateDataIncluded = includeProcessStateData;
|
|
|
|
int columnIndex = firstColumn;
|
|
|
|
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++
|
|
: POWER_MODEL_NOT_INCLUDED, // 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_UNSPECIFIED) {
|
|
continue;
|
|
}
|
|
|
|
perComponentKeys.add(
|
|
new Key(componentId, processState,
|
|
powerModelsIncluded
|
|
? columnIndex++
|
|
: POWER_MODEL_NOT_INCLUDED, // power model
|
|
columnIndex++, // power
|
|
columnIndex++ // usage duration
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
keys[componentId] = perComponentKeys.toArray(KEY_ARRAY);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
processStateKeys[processState] = perProcStateKeys.toArray(KEY_ARRAY);
|
|
}
|
|
} else {
|
|
processStateKeys = null;
|
|
}
|
|
|
|
firstCustomConsumedPowerColumn = columnIndex;
|
|
columnIndex += customPowerComponentCount;
|
|
|
|
firstCustomUsageDurationColumn = columnIndex;
|
|
columnIndex += customPowerComponentCount;
|
|
|
|
columnCount = columnIndex;
|
|
}
|
|
}
|
|
|
|
static BatteryConsumerDataLayout createBatteryConsumerDataLayout(
|
|
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, includeProcessStateData);
|
|
}
|
|
|
|
protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
|
|
protected final BatteryConsumer.BatteryConsumerData mData;
|
|
protected final PowerComponents.Builder mPowerComponentsBuilder;
|
|
|
|
public BaseBuilder(BatteryConsumer.BatteryConsumerData data, int consumerType,
|
|
double minConsumedPowerThreshold) {
|
|
mData = data;
|
|
data.putLong(COLUMN_INDEX_BATTERY_CONSUMER_TYPE, consumerType);
|
|
|
|
mPowerComponentsBuilder = new PowerComponents.Builder(data, minConsumedPowerThreshold);
|
|
}
|
|
|
|
@Nullable
|
|
public Key[] getKeys(@PowerComponent int componentId) {
|
|
return mData.getKeys(componentId);
|
|
}
|
|
|
|
@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.
|
|
*
|
|
* @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(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
|
|
componentPower, powerModel);
|
|
return (T) this;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@NonNull
|
|
public T addConsumedPower(@PowerComponent int componentId, double componentPower,
|
|
@PowerModel int powerModel) {
|
|
mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
|
|
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;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@NonNull
|
|
public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
|
|
mPowerComponentsBuilder.addConsumedPower(key, 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(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
|
|
componentUsageTimeMillis);
|
|
return (T) this;
|
|
}
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@NonNull
|
|
public T setUsageDurationMillis(Key key, long componentUsageTimeMillis) {
|
|
mPowerComponentsBuilder.setUsageDurationMillis(key, 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();
|
|
}
|
|
}
|
|
}
|