diff options
3 files changed, 140 insertions, 21 deletions
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index 4b12aa6a2634..e8631119bbc6 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -223,22 +223,32 @@ class PowerComponents { for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; componentId++) { - final BatteryConsumer.Key key = mData.getKey(componentId, PROCESS_STATE_ANY); - final long powerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower(key)); - final long durationMs = getUsageDurationMillis(key); + final BatteryConsumer.Key[] keys = mData.getKeys(componentId); + for (BatteryConsumer.Key key : keys) { + final long powerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower(key)); + final long durationMs = getUsageDurationMillis(key); - if (powerDeciCoulombs == 0 && durationMs == 0) { - // No interesting data. Make sure not to even write the COMPONENT int. - continue; - } + 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; - } + interestingData = true; + if (proto == null) { + // We're just asked whether there is data, not to actually write it. + // And there is. + return true; + } - writePowerComponent(proto, componentId, powerDeciCoulombs, durationMs); + if (key.processState == PROCESS_STATE_ANY) { + writePowerComponentUsage(proto, + BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS, + componentId, powerDeciCoulombs, durationMs); + } else { + writePowerUsageSlice(proto, componentId, powerDeciCoulombs, durationMs, + key.processState); + } + } } for (int idx = 0; idx < mData.layout.customPowerComponentCount; idx++) { final int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + idx; @@ -257,15 +267,49 @@ class PowerComponents { return true; } - writePowerComponent(proto, componentId, powerDeciCoulombs, durationMs); + writePowerComponentUsage(proto, + BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS, + componentId, powerDeciCoulombs, durationMs); } return interestingData; } - private void writePowerComponent(ProtoOutputStream proto, int componentId, + private void writePowerUsageSlice(ProtoOutputStream proto, int componentId, + long powerDeciCoulombs, long durationMs, int processState) { + final long slicesToken = + proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.SLICES); + writePowerComponentUsage(proto, + BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice + .POWER_COMPONENT, + componentId, powerDeciCoulombs, durationMs); + + final int procState; + switch (processState) { + case BatteryConsumer.PROCESS_STATE_FOREGROUND: + procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice + .FOREGROUND; + break; + case BatteryConsumer.PROCESS_STATE_BACKGROUND: + procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice + .BACKGROUND; + break; + case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE: + procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice + .FOREGROUND_SERVICE; + break; + default: + throw new IllegalArgumentException("Unknown process state: " + processState); + } + + proto.write(BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice + .PROCESS_STATE, procState); + + proto.end(slicesToken); + } + + private void writePowerComponentUsage(ProtoOutputStream proto, long tag, int componentId, long powerDeciCoulombs, long durationMs) { - final long token = - proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS); + final long token = proto.start(tag); proto.write( BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage .COMPONENT, diff --git a/core/proto/android/os/batteryusagestats.proto b/core/proto/android/os/batteryusagestats.proto index bcce784ae0be..c0a9f0339d9e 100644 --- a/core/proto/android/os/batteryusagestats.proto +++ b/core/proto/android/os/batteryusagestats.proto @@ -62,6 +62,26 @@ message BatteryUsageStatsAtomsProto { optional int64 duration_millis = 3; } repeated PowerComponentUsage power_components = 2; + + // Represents a slice of power attribution, e.g. "cpu while in the background" + // or "wifi when running a background service". Queries that care about + // PowerComponentUsage slices need to be aware of all supported dimensions. + // There are no roll-ups included in the slices - it is up to the clients + // of this data to aggregate values as needed. + message PowerComponentUsageSlice { + optional PowerComponentUsage power_component = 1; + + enum ProcessState { + UNSPECIFIED = 0; + FOREGROUND = 1; + BACKGROUND = 2; + FOREGROUND_SERVICE = 3; + } + + optional ProcessState process_state = 2; + } + + repeated PowerComponentUsageSlice slices = 3; } // Total power usage for the device during this session. diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java index 7ff76df17615..e230a54e261f 100644 --- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java +++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.os.BatteryConsumer; import android.os.BatteryUsageStats; +import android.os.UidBatteryConsumer; import android.os.nano.BatteryUsageStatsAtomsProto; import android.os.nano.BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage; @@ -134,6 +135,43 @@ public class BatteryUsageStatsPulledTest { componentProto.durationMillis); } } + + for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; + componentId++) { + final BatteryConsumer.Key[] keys = consumer.getKeys(componentId); + if (keys == null || keys.length <= 1) { + continue; + } + + for (BatteryConsumer.Key key : keys) { + if (key.processState == 0) { + continue; + } + + BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice + sliceProto = null; + for (BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice + slice : consumerProto.slices) { + if (slice.powerComponent.component == componentId + && slice.processState == key.processState) { + sliceProto = slice; + break; + } + } + + final long expectedPowerDc = convertMahToDc(consumer.getConsumedPower(key)); + final long expectedUsageDurationMillis = consumer.getUsageDurationMillis(key); + if (expectedPowerDc == 0 && expectedUsageDurationMillis == 0) { + assertThat(sliceProto).isNull(); + } else { + assertThat(sliceProto).isNotNull(); + assertThat(sliceProto.powerComponent.powerDeciCoulombs) + .isEqualTo(expectedPowerDc); + assertThat(sliceProto.powerComponent.durationMillis) + .isEqualTo(expectedUsageDurationMillis); + } + } + } } private void assertSameUidBatteryConsumer( @@ -172,14 +210,17 @@ public class BatteryUsageStatsPulledTest { final BatteryStatsImpl.Uid batteryStatsUid3 = batteryStats.getUidStatsLocked(UID_3); final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"}) + new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"}, + /* includePowerModels */ true, + /* includeProcessStats */true) .setDischargePercentage(20) .setDischargedPowerRange(1000, 2000) .setStatsStartTimestamp(1000); - builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid0) + final UidBatteryConsumer.Builder uidBuilder = builder.getOrCreateUidBatteryConsumerBuilder( + batteryStatsUid0) .setPackageWithHighestDrain("myPackage0") - .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1000) - .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 2000) + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000) .setConsumedPower( BatteryConsumer.POWER_COMPONENT_SCREEN, 300) .setConsumedPower( @@ -193,6 +234,20 @@ public class BatteryUsageStatsPulledTest { .setUsageDurationForCustomComponentMillis( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 800); + final BatteryConsumer.Key keyFg = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_FOREGROUND); + final BatteryConsumer.Key keyBg = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_BACKGROUND); + final BatteryConsumer.Key keyFgs = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE); + + uidBuilder.setConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .setUsageDurationMillis(keyFg, 8100) + .setConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) + .setUsageDurationMillis(keyBg, 8200) + .setConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) + .setUsageDurationMillis(keyFgs, 8300); + builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid1) .setPackageWithHighestDrain("myPackage1") .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1234); |