Loading core/java/android/os/BatteryConsumer.java +73 −18 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading core/java/android/os/BatteryUsageStats.java +67 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. */ Loading core/java/android/os/PowerComponents.java +57 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading core/proto/android/os/batteryusagestats.proto 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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. */ syntax = "proto2"; package android.os; option java_multiple_files = true; import "frameworks/proto_logging/stats/enums/os/enums.proto"; // This message is used for statsd logging and should be kept in sync with // frameworks/proto_logging/stats/atoms.proto /** * Represents a device's BatteryUsageStats, with power usage information about the device * and each app. */ message BatteryUsageStatsAtomsProto { // The session start timestamp in UTC milliseconds since January 1, 1970, per Date#getTime(). // All data is no older than this time. optional int64 session_start_millis = 1; // The session end timestamp in UTC milliseconds since January 1, 1970, per Date#getTime(). // All data is no more recent than this time. optional int64 session_end_millis = 2; // Length that the reported data covered. This usually will be equal to the entire session, // session_end_millis - session_start_millis, but may not be if some data during this time frame // is missing. optional int64 session_duration_millis = 3; // Represents usage of a consumer, storing all of its power component usage. message BatteryConsumerData { // Total power consumed by this BatteryConsumer (including all of its PowerComponents). // May not equal the sum of the PowerComponentUsage due to under- or over-estimations. // Multiply by 1/36 to obtain mAh. optional int64 total_consumed_power_deci_coulombs = 1; // Represents power and time usage of a particular power component. message PowerComponentUsage { // Holds android.os.PowerComponentEnum, or custom component value between 1000 and 9999. // Evidently, if one attempts to write an int to an enum field that is out of range, it // is treated as 0, so we must make this an int32. optional int32 component = 1; // Power consumed by this component. Multiply by 1/36 to obtain mAh. optional int64 power_deci_coulombs = 2; optional int64 duration_millis = 3; } repeated PowerComponentUsage power_components = 2; } // Total power usage for the device during this session. optional BatteryConsumerData device_battery_consumer = 4; // Power usage by a uid during this session. message UidBatteryConsumer { optional int32 uid = 1; optional BatteryConsumerData battery_consumer_data = 2; optional int64 time_in_foreground_millis = 3; optional int64 time_in_background_millis = 4; } repeated UidBatteryConsumer uid_battery_consumers = 5; // Sum of all discharge percentage point drops during the reported session. optional int32 session_discharge_percentage = 6; } No newline at end of file core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp 0 → 100644 +28 −0 Original line number Diff line number Diff line package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_base_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], } android_test { name: "BatteryUsageStatsProtoTests", srcs: ["src/**/*.java"], static_libs: [ "androidx.test.rules", "junit", "platform-test-annotations", "platformprotosnano", "statsdprotolite", ], libs: ["android.test.runner"], platform_apis: true, certificate: "platform", test_suites: ["device-tests"], } Loading
core/java/android/os/BatteryConsumer.java +73 −18 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading
core/java/android/os/BatteryUsageStats.java +67 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. */ Loading
core/java/android/os/PowerComponents.java +57 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading
core/proto/android/os/batteryusagestats.proto 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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. */ syntax = "proto2"; package android.os; option java_multiple_files = true; import "frameworks/proto_logging/stats/enums/os/enums.proto"; // This message is used for statsd logging and should be kept in sync with // frameworks/proto_logging/stats/atoms.proto /** * Represents a device's BatteryUsageStats, with power usage information about the device * and each app. */ message BatteryUsageStatsAtomsProto { // The session start timestamp in UTC milliseconds since January 1, 1970, per Date#getTime(). // All data is no older than this time. optional int64 session_start_millis = 1; // The session end timestamp in UTC milliseconds since January 1, 1970, per Date#getTime(). // All data is no more recent than this time. optional int64 session_end_millis = 2; // Length that the reported data covered. This usually will be equal to the entire session, // session_end_millis - session_start_millis, but may not be if some data during this time frame // is missing. optional int64 session_duration_millis = 3; // Represents usage of a consumer, storing all of its power component usage. message BatteryConsumerData { // Total power consumed by this BatteryConsumer (including all of its PowerComponents). // May not equal the sum of the PowerComponentUsage due to under- or over-estimations. // Multiply by 1/36 to obtain mAh. optional int64 total_consumed_power_deci_coulombs = 1; // Represents power and time usage of a particular power component. message PowerComponentUsage { // Holds android.os.PowerComponentEnum, or custom component value between 1000 and 9999. // Evidently, if one attempts to write an int to an enum field that is out of range, it // is treated as 0, so we must make this an int32. optional int32 component = 1; // Power consumed by this component. Multiply by 1/36 to obtain mAh. optional int64 power_deci_coulombs = 2; optional int64 duration_millis = 3; } repeated PowerComponentUsage power_components = 2; } // Total power usage for the device during this session. optional BatteryConsumerData device_battery_consumer = 4; // Power usage by a uid during this session. message UidBatteryConsumer { optional int32 uid = 1; optional BatteryConsumerData battery_consumer_data = 2; optional int64 time_in_foreground_millis = 3; optional int64 time_in_background_millis = 4; } repeated UidBatteryConsumer uid_battery_consumers = 5; // Sum of all discharge percentage point drops during the reported session. optional int32 session_discharge_percentage = 6; } No newline at end of file
core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp 0 → 100644 +28 −0 Original line number Diff line number Diff line package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_base_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], } android_test { name: "BatteryUsageStatsProtoTests", srcs: ["src/**/*.java"], static_libs: [ "androidx.test.rules", "junit", "platform-test-annotations", "platformprotosnano", "statsdprotolite", ], libs: ["android.test.runner"], platform_apis: true, certificate: "platform", test_suites: ["device-tests"], }