Loading core/java/android/os/PowerComponents.java +61 −17 Original line number Diff line number Diff line Loading @@ -223,7 +223,8 @@ class PowerComponents { for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; componentId++) { final BatteryConsumer.Key key = mData.getKey(componentId, PROCESS_STATE_ANY); final BatteryConsumer.Key[] keys = mData.getKeys(componentId); for (BatteryConsumer.Key key : keys) { final long powerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower(key)); final long durationMs = getUsageDurationMillis(key); Loading @@ -234,11 +235,20 @@ class PowerComponents { interestingData = true; if (proto == null) { // We're just asked whether there is data, not to actually write it. And there is. // 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; Loading @@ -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, Loading core/proto/android/os/batteryusagestats.proto +20 −0 Original line number Diff line number Diff line Loading @@ -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. Loading core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java +59 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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( Loading Loading @@ -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( Loading @@ -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); Loading Loading
core/java/android/os/PowerComponents.java +61 −17 Original line number Diff line number Diff line Loading @@ -223,7 +223,8 @@ class PowerComponents { for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; componentId++) { final BatteryConsumer.Key key = mData.getKey(componentId, PROCESS_STATE_ANY); final BatteryConsumer.Key[] keys = mData.getKeys(componentId); for (BatteryConsumer.Key key : keys) { final long powerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower(key)); final long durationMs = getUsageDurationMillis(key); Loading @@ -234,11 +235,20 @@ class PowerComponents { interestingData = true; if (proto == null) { // We're just asked whether there is data, not to actually write it. And there is. // 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; Loading @@ -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, Loading
core/proto/android/os/batteryusagestats.proto +20 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java +59 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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( Loading Loading @@ -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( Loading @@ -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); Loading