diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2021-05-21 16:22:42 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-05-21 16:22:42 +0000 |
| commit | e4cc52accedde997b8509764ea512ceefeabea9f (patch) | |
| tree | f28706ff1ad8038f96cd30f1988774c52f166e8f /core/java/android | |
| parent | 36da5805e5cbfc6c7b69a6639ebd36817e5fba82 (diff) | |
| parent | 2f3abb44411db58e98b64644adee9633fc504e7d (diff) | |
Merge "BatteryUsageStats atom - frameworks/base" into sc-dev
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/os/BatteryConsumer.java | 91 | ||||
| -rw-r--r-- | core/java/android/os/BatteryUsageStats.java | 68 | ||||
| -rw-r--r-- | core/java/android/os/PowerComponents.java | 57 |
3 files changed, 197 insertions, 19 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index edb30b0f26ca..da94d74e5ae8 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -18,6 +18,8 @@ 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; @@ -59,27 +61,32 @@ public abstract class BatteryConsumer { public static @interface PowerComponent { } - public static final int POWER_COMPONENT_SCREEN = 0; - public static final int POWER_COMPONENT_CPU = 1; - public static final int POWER_COMPONENT_BLUETOOTH = 2; - public static final int POWER_COMPONENT_CAMERA = 3; - public static final int POWER_COMPONENT_AUDIO = 4; - public static final int POWER_COMPONENT_VIDEO = 5; - public static final int POWER_COMPONENT_FLASHLIGHT = 6; - public static final int POWER_COMPONENT_SYSTEM_SERVICES = 7; - public static final int POWER_COMPONENT_MOBILE_RADIO = 8; - public static final int POWER_COMPONENT_SENSORS = 9; - public static final int POWER_COMPONENT_GNSS = 10; - public static final int POWER_COMPONENT_WIFI = 11; - public static final int POWER_COMPONENT_WAKELOCK = 12; - public static final int POWER_COMPONENT_MEMORY = 13; - public static final int POWER_COMPONENT_PHONE = 14; - public static final int POWER_COMPONENT_AMBIENT_DISPLAY = 15; - public static final int POWER_COMPONENT_IDLE = 16; + 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 = 17; + 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; @@ -263,6 +270,54 @@ public abstract class BatteryConsumer { */ 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<T extends BaseBuilder<?>> { final PowerComponents.Builder mPowerComponentsBuilder; diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 8f366636ed9f..370052d47d16 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.util.Range; import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; import com.android.internal.os.BatteryStatsHistory; import com.android.internal.os.BatteryStatsHistoryIterator; @@ -184,7 +185,8 @@ public final class BatteryUsageStats implements Parcelable { /** * Portion of battery charge drained since BatteryStats reset (e.g. due to being fully - * charged), as percentage of the full charge in the range [0:100] + * charged), as percentage of the full charge in the range [0:100]. May exceed 100 if + * the device repeatedly charged and discharged prior to the reset. */ public int getDischargePercentage() { return mDischargePercentage; @@ -365,6 +367,70 @@ public final class BatteryUsageStats implements Parcelable { } }; + /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */ + public byte[] getStatsProto(long sessionEndTimestampMs) { + + final long sessionStartMillis = getStatsStartTimestamp(); + // TODO(b/187223764): Use the getStatsEndTimestamp() instead, once that is added. + final long sessionEndMillis = sessionEndTimestampMs; + final long sessionDurationMillis = sessionEndTimestampMs - getStatsStartTimestamp(); + + final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer( + AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); + + final int sessionDischargePercentage = getDischargePercentage(); + + final ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, sessionStartMillis); + proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, sessionEndMillis); + proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, sessionDurationMillis); + deviceBatteryConsumer.writeStatsProto(proto, + BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER); + writeUidBatteryConsumersProto(proto); + proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE, + sessionDischargePercentage); + return proto.getBytes(); + } + + /** + * Writes the UidBatteryConsumers data, held by this BatteryUsageStats, to the proto (as used + * for atoms.proto). + */ + private void writeUidBatteryConsumersProto(ProtoOutputStream proto) { + final List<UidBatteryConsumer> consumers = getUidBatteryConsumers(); + + // TODO: Sort the list by power consumption. If during the for, proto.getRawSize() > 45kb, + // truncate the remainder of the list. + final int size = consumers.size(); + for (int i = 0; i < size; i++) { + final UidBatteryConsumer consumer = consumers.get(i); + + final long fgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND); + final long bgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND); + final boolean hasBaseData = consumer.hasStatsProtoData(); + + if (fgMs == 0 && bgMs == 0 && !hasBaseData) { + continue; + } + + final long token = proto.start(BatteryUsageStatsAtomsProto.UID_BATTERY_CONSUMERS); + proto.write( + BatteryUsageStatsAtomsProto.UidBatteryConsumer.UID, + consumer.getUid()); + if (hasBaseData) { + consumer.writeStatsProto(proto, + BatteryUsageStatsAtomsProto.UidBatteryConsumer.BATTERY_CONSUMER_DATA); + } + proto.write( + BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_FOREGROUND_MILLIS, + fgMs); + proto.write( + BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS, + bgMs); + proto.end(token); + } + } + /** * Prints the stats in a human-readable format. */ diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index 964f1b681866..a90ed20d54fc 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -15,7 +15,11 @@ */ package android.os; +import static android.os.BatteryConsumer.convertMahToDeciCoulombs; + import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.proto.ProtoOutputStream; import com.android.internal.os.PowerCalculator; @@ -237,6 +241,59 @@ class PowerComponents { } } + /** Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto. */ + boolean hasStatsProtoData() { + return writeStatsProtoImpl(null); + } + + /** Writes all atoms.proto POWER_COMPONENTS for this PowerComponents to the given proto. */ + void writeStatsProto(@NonNull ProtoOutputStream proto) { + writeStatsProtoImpl(proto); + } + + /** + * Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto, + * and writes it to the given proto if it is non-null. + */ + private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto) { + boolean interestingData = false; + + for (int idx = 0; idx < mPowerComponentsMah.length; idx++) { + final int componentId = idx < BatteryConsumer.POWER_COMPONENT_COUNT ? + idx : idx - CUSTOM_POWER_COMPONENT_OFFSET; + final long powerDeciCoulombs = convertMahToDeciCoulombs(mPowerComponentsMah[idx]); + final long durationMs = mUsageDurationsMs[idx]; + + if (powerDeciCoulombs == 0 && durationMs == 0) { + // No interesting data. Make sure not to even write the COMPONENT int. + continue; + } + + interestingData = true; + 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(BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS); + proto.write( + BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage + .COMPONENT, + componentId); + proto.write( + BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage + .POWER_DECI_COULOMBS, + powerDeciCoulombs); + proto.write( + BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage + .DURATION_MILLIS, + durationMs); + proto.end(token); + } + return interestingData; + } + /** * Builder for PowerComponents. */ |
