Loading core/java/com/android/internal/os/PowerStats.java +186 −13 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.Bundle; import android.os.Parcel; import android.os.PersistableBundle; Loading @@ -34,8 +35,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for Loading Loading @@ -75,6 +80,10 @@ public final class PowerStats { */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public static class Descriptor { public static final String EXTRA_DEVICE_STATS_FORMAT = "format-device"; public static final String EXTRA_STATE_STATS_FORMAT = "format-state"; public static final String EXTRA_UID_STATS_FORMAT = "format-uid"; public static final String XML_TAG_DESCRIPTOR = "descriptor"; private static final String XML_ATTR_ID = "id"; private static final String XML_ATTR_NAME = "name"; Loading Loading @@ -120,6 +129,10 @@ public final class PowerStats { */ public final PersistableBundle extras; private PowerStatsFormatter mDeviceStatsFormatter; private PowerStatsFormatter mStateStatsFormatter; private PowerStatsFormatter mUidStatsFormatter; public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId, int statsArrayLength, @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength, int uidStatsArrayLength, Loading @@ -131,7 +144,7 @@ public final class PowerStats { public Descriptor(int customPowerComponentId, String name, int statsArrayLength, @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength, int uidStatsArrayLength, PersistableBundle extras) { int uidStatsArrayLength, @NonNull PersistableBundle extras) { if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH); Loading @@ -153,6 +166,39 @@ public final class PowerStats { this.extras = extras; } /** * Returns a custom formatter for this type of power stats. */ public PowerStatsFormatter getDeviceStatsFormatter() { if (mDeviceStatsFormatter == null) { mDeviceStatsFormatter = new PowerStatsFormatter( extras.getString(EXTRA_DEVICE_STATS_FORMAT)); } return mDeviceStatsFormatter; } /** * Returns a custom formatter for this type of power stats, specifically per-state stats. */ public PowerStatsFormatter getStateStatsFormatter() { if (mStateStatsFormatter == null) { mStateStatsFormatter = new PowerStatsFormatter( extras.getString(EXTRA_STATE_STATS_FORMAT)); } return mStateStatsFormatter; } /** * Returns a custom formatter for this type of power stats, specifically per-UID stats. */ public PowerStatsFormatter getUidStatsFormatter() { if (mUidStatsFormatter == null) { mUidStatsFormatter = new PowerStatsFormatter( extras.getString(EXTRA_UID_STATS_FORMAT)); } return mUidStatsFormatter; } /** * Returns the label associated with the give state key, e.g. "5G-high" for the * state of Mobile Radio representing the 5G mode and high signal power. Loading Loading @@ -491,20 +537,22 @@ public final class PowerStats { StringBuilder sb = new StringBuilder(); sb.append("duration=").append(durationMs).append(" ").append(descriptor.name); if (stats.length > 0) { sb.append("=").append(Arrays.toString(stats)); sb.append("=").append(descriptor.getDeviceStatsFormatter().format(stats)); } if (descriptor.stateStatsArrayLength != 0) { PowerStatsFormatter formatter = descriptor.getStateStatsFormatter(); for (int i = 0; i < stateStats.size(); i++) { sb.append(" ["); sb.append(" ("); sb.append(descriptor.getStateLabel(stateStats.keyAt(i))); sb.append("]="); sb.append(Arrays.toString(stateStats.valueAt(i))); sb.append(") "); sb.append(formatter.format(stateStats.valueAt(i))); } } PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter(); for (int i = 0; i < uidStats.size(); i++) { sb.append(uidPrefix) .append(UserHandle.formatUid(uidStats.keyAt(i))) .append(": ").append(Arrays.toString(uidStats.valueAt(i))); .append(": ").append(uidStatsFormatter.format(uidStats.valueAt(i))); } return sb.toString(); } Loading @@ -513,26 +561,29 @@ public final class PowerStats { * Prints the contents of the stats snapshot. */ public void dump(IndentingPrintWriter pw) { pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')'); pw.println(descriptor.name + " (" + descriptor.powerComponentId + ')'); pw.increaseIndent(); pw.print("duration", durationMs).println(); if (descriptor.statsArrayLength != 0) { pw.print("stats", Arrays.toString(stats)).println(); pw.println(descriptor.getDeviceStatsFormatter().format(stats)); } if (descriptor.stateStatsArrayLength != 0) { PowerStatsFormatter formatter = descriptor.getStateStatsFormatter(); for (int i = 0; i < stateStats.size(); i++) { pw.print("state "); pw.print(" ("); pw.print(descriptor.getStateLabel(stateStats.keyAt(i))); pw.print(": "); pw.print(Arrays.toString(stateStats.valueAt(i))); pw.print(") "); pw.print(formatter.format(stateStats.valueAt(i))); pw.println(); } } PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter(); for (int i = 0; i < uidStats.size(); i++) { pw.print("UID "); pw.print(uidStats.keyAt(i)); pw.print(UserHandle.formatUid(uidStats.keyAt(i))); pw.print(": "); pw.print(Arrays.toString(uidStats.valueAt(i))); pw.print(uidStatsFormatter.format(uidStats.valueAt(i))); pw.println(); } pw.decreaseIndent(); Loading @@ -542,4 +593,126 @@ public final class PowerStats { public String toString() { return "PowerStats: " + formatForBatteryHistory(" UID "); } public static class PowerStatsFormatter { private static class Section { public String label; public int position; public int length; public boolean optional; public boolean typePower; } private static final double NANO_TO_MILLI_MULTIPLIER = 1.0 / 1000000.0; private static final Pattern SECTION_PATTERN = Pattern.compile("([^:]+):(\\d+)(\\[(?<L>\\d+)])?(?<F>\\S*)\\s*"); private final List<Section> mSections; public PowerStatsFormatter(String format) { mSections = parseFormat(format); } /** * Produces a formatted string representing the supplied array, with labels * and other adornments specific to the power stats layout. */ public String format(long[] stats) { return format(mSections, stats); } private List<Section> parseFormat(String format) { if (format == null || format.isBlank()) { return null; } ArrayList<Section> sections = new ArrayList<>(); Matcher matcher = SECTION_PATTERN.matcher(format); for (int position = 0; position < format.length(); position = matcher.end()) { if (!matcher.find() || matcher.start() != position) { Slog.wtf(TAG, "Bad power stats format '" + format + "'"); return null; } Section section = new Section(); section.label = matcher.group(1); section.position = Integer.parseUnsignedInt(matcher.group(2)); String length = matcher.group("L"); if (length != null) { section.length = Integer.parseUnsignedInt(length); } else { section.length = 1; } String flags = matcher.group("F"); if (flags != null) { for (int i = 0; i < flags.length(); i++) { char flag = flags.charAt(i); switch (flag) { case '?': section.optional = true; break; case 'p': section.typePower = true; break; default: Slog.e(TAG, "Unsupported format option '" + flag + "' in " + format); break; } } } sections.add(section); } return sections; } private String format(List<Section> sections, long[] stats) { if (sections == null) { return Arrays.toString(stats); } StringBuilder sb = new StringBuilder(); for (int i = 0, count = sections.size(); i < count; i++) { Section section = sections.get(i); if (section.length == 0) { continue; } if (section.optional) { boolean nonZero = false; for (int offset = 0; offset < section.length; offset++) { if (stats[section.position + offset] != 0) { nonZero = true; break; } } if (!nonZero) { continue; } } if (!sb.isEmpty()) { sb.append(' '); } sb.append(section.label).append(": "); if (section.length != 1) { sb.append('['); } for (int offset = 0; offset < section.length; offset++) { if (offset != 0) { sb.append(", "); } if (section.typePower) { sb.append(BatteryStats.formatCharge( stats[section.position + offset] * NANO_TO_MILLI_MULTIPLIER)); } else { sb.append(stats[section.position + offset]); } } if (section.length != 1) { sb.append(']'); } } return sb.toString(); } } } core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java +71 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.os.BatteryConsumer; import android.os.Parcel; import android.os.PersistableBundle; import android.platform.test.ravenwood.RavenwoodRule; import android.util.IndentingPrintWriter; import android.util.SparseArray; import android.util.Xml; Loading @@ -31,6 +32,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.google.common.truth.StringSubject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -38,6 +41,7 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.StringWriter; import java.nio.charset.StandardCharsets; @RunWith(AndroidJUnit4.class) Loading @@ -56,6 +60,9 @@ public class PowerStatsTest { extras.putBoolean("hasPowerMonitor", true); SparseArray<String> stateLabels = new SparseArray<>(); stateLabels.put(0x0F, "idle"); extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, "device:0[3]"); extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, "state:0"); extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, "a:0 b:1"); mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, stateLabels, 1, 2, extras); mRegistry.register(mDescriptor); Loading Loading @@ -191,4 +198,68 @@ public class PowerStatsTest { newParcel.setDataPosition(0); return newParcel; } @Test public void formatForBatteryHistory() { PowerStats stats = new PowerStats(mDescriptor); stats.durationMs = 1234; stats.stats[0] = 10; stats.stats[1] = 20; stats.stats[2] = 30; stats.stateStats.put(0x0F, new long[]{16}); stats.stateStats.put(0xF0, new long[]{17}); stats.uidStats.put(42, new long[]{40, 50}); stats.uidStats.put(99, new long[]{60, 70}); assertThat(stats.formatForBatteryHistory(" #")) .isEqualTo("duration=1234 cpu=" + "device: [10, 20, 30]" + " (idle) state: 16" + " (cpu-f0) state: 17" + " #42: a: 40 b: 50" + " #99: a: 60 b: 70"); } @Test public void dump() { PowerStats stats = new PowerStats(mDescriptor); stats.durationMs = 1234; stats.stats[0] = 10; stats.stats[1] = 20; stats.stats[2] = 30; stats.stateStats.put(0x0F, new long[]{16}); stats.stateStats.put(0xF0, new long[]{17}); stats.uidStats.put(42, new long[]{40, 50}); stats.uidStats.put(99, new long[]{60, 70}); StringWriter sw = new StringWriter(); IndentingPrintWriter pw = new IndentingPrintWriter(sw); stats.dump(pw); pw.flush(); String dump = sw.toString(); assertThat(dump).contains("duration=1234"); assertThat(dump).contains("device: [10, 20, 30]"); assertThat(dump).contains("(idle) state: 16"); assertThat(dump).contains("(cpu-f0) state: 17"); assertThat(dump).contains("UID 42: a: 40 b: 50"); assertThat(dump).contains("UID 99: a: 60 b: 70"); } @Test public void formatter() { assertThatFormatted(new long[]{12, 34, 56}, "a:0 b:1[2]") .isEqualTo("a: 12 b: [34, 56]"); assertThatFormatted(new long[]{12, 0, 0}, "a:0? b:1[2]?") .isEqualTo("a: 12"); assertThatFormatted(new long[]{0, 34, 56}, "a:0? b:1[2]?") .isEqualTo("b: [34, 56]"); assertThatFormatted(new long[]{3141592, 2000000, 1414213}, "pi:0p sqrt:1[2]p") .isEqualTo("pi: 3.14 sqrt: [2.00, 1.41]"); } private static StringSubject assertThatFormatted(long[] stats, String format) { return assertThat(new PowerStats.PowerStatsFormatter(format) .format(stats)); } } services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java +5 −24 Original line number Diff line number Diff line Loading @@ -19,12 +19,9 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.os.BatteryConsumer; import com.android.internal.os.PowerStats; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** Loading Loading @@ -206,25 +203,9 @@ public class AggregatedPowerStatsConfig { return mPowerComponents; } private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() { private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() { @Override void finish(PowerComponentAggregatedPowerStats stats) { } @Override String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { return Arrays.toString(stats); } @Override String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { return descriptor.getStateLabel(key) + " " + Arrays.toString(stats); } @Override String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { return Arrays.toString(stats); } }; } services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java +3 −3 Original line number Diff line number Diff line Loading @@ -44,7 +44,7 @@ public class CpuPowerStatsLayout extends PowerStatsLayout { * Declare that the stats array has a section capturing CPU time per scaling step */ public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) { mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount); mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount, "steps"); mDeviceCpuTimeByScalingStepCount = scalingStepCount; } Loading Loading @@ -72,7 +72,7 @@ public class CpuPowerStatsLayout extends PowerStatsLayout { * Declare that the stats array has a section capturing CPU time in each cluster */ public void addDeviceSectionCpuTimeByCluster(int clusterCount) { mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount); mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount, "clusters"); mDeviceCpuTimeByClusterCount = clusterCount; } Loading Loading @@ -102,7 +102,7 @@ public class CpuPowerStatsLayout extends PowerStatsLayout { public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) { mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap; updatePowerBracketCount(); mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount); mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount, "time"); } private void updatePowerBracketCount() { Loading services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java +0 −61 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.power.stats; import android.os.BatteryStats; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -487,64 +486,4 @@ public class CpuPowerStatsProcessor extends PowerStatsProcessor { stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); } } @Override public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { unpackPowerStatsDescriptor(descriptor); StringBuilder sb = new StringBuilder(); int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount(); sb.append("steps: ["); for (int step = 0; step < cpuScalingStepCount; step++) { if (step != 0) { sb.append(", "); } sb.append(mStatsLayout.getTimeByScalingStep(stats, step)); } int clusterCount = mStatsLayout.getCpuClusterCount(); sb.append("] clusters: ["); for (int cluster = 0; cluster < clusterCount; cluster++) { if (cluster != 0) { sb.append(", "); } sb.append(mStatsLayout.getTimeByCluster(stats, cluster)); } sb.append("] uptime: ").append(mStatsLayout.getUsageDuration(stats)); int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); if (energyConsumerCount > 0) { sb.append(" energy: ["); for (int i = 0; i < energyConsumerCount; i++) { if (i != 0) { sb.append(", "); } sb.append(mStatsLayout.getConsumedEnergy(stats, i)); } sb.append("]"); } sb.append(" power: ").append( BatteryStats.formatCharge(mStatsLayout.getDevicePowerEstimate(stats))); return sb.toString(); } @Override String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { // Unsupported for this power component return null; } @Override public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { unpackPowerStatsDescriptor(descriptor); StringBuilder sb = new StringBuilder(); sb.append("["); int powerBracketCount = mStatsLayout.getCpuPowerBracketCount(); for (int bracket = 0; bracket < powerBracketCount; bracket++) { if (bracket != 0) { sb.append(", "); } sb.append(mStatsLayout.getUidTimeByPowerBracket(stats, bracket)); } sb.append("] power: ").append( BatteryStats.formatCharge(mStatsLayout.getUidPowerEstimate(stats))); return sb.toString(); } } Loading
core/java/com/android/internal/os/PowerStats.java +186 −13 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.Bundle; import android.os.Parcel; import android.os.PersistableBundle; Loading @@ -34,8 +35,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for Loading Loading @@ -75,6 +80,10 @@ public final class PowerStats { */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public static class Descriptor { public static final String EXTRA_DEVICE_STATS_FORMAT = "format-device"; public static final String EXTRA_STATE_STATS_FORMAT = "format-state"; public static final String EXTRA_UID_STATS_FORMAT = "format-uid"; public static final String XML_TAG_DESCRIPTOR = "descriptor"; private static final String XML_ATTR_ID = "id"; private static final String XML_ATTR_NAME = "name"; Loading Loading @@ -120,6 +129,10 @@ public final class PowerStats { */ public final PersistableBundle extras; private PowerStatsFormatter mDeviceStatsFormatter; private PowerStatsFormatter mStateStatsFormatter; private PowerStatsFormatter mUidStatsFormatter; public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId, int statsArrayLength, @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength, int uidStatsArrayLength, Loading @@ -131,7 +144,7 @@ public final class PowerStats { public Descriptor(int customPowerComponentId, String name, int statsArrayLength, @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength, int uidStatsArrayLength, PersistableBundle extras) { int uidStatsArrayLength, @NonNull PersistableBundle extras) { if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH); Loading @@ -153,6 +166,39 @@ public final class PowerStats { this.extras = extras; } /** * Returns a custom formatter for this type of power stats. */ public PowerStatsFormatter getDeviceStatsFormatter() { if (mDeviceStatsFormatter == null) { mDeviceStatsFormatter = new PowerStatsFormatter( extras.getString(EXTRA_DEVICE_STATS_FORMAT)); } return mDeviceStatsFormatter; } /** * Returns a custom formatter for this type of power stats, specifically per-state stats. */ public PowerStatsFormatter getStateStatsFormatter() { if (mStateStatsFormatter == null) { mStateStatsFormatter = new PowerStatsFormatter( extras.getString(EXTRA_STATE_STATS_FORMAT)); } return mStateStatsFormatter; } /** * Returns a custom formatter for this type of power stats, specifically per-UID stats. */ public PowerStatsFormatter getUidStatsFormatter() { if (mUidStatsFormatter == null) { mUidStatsFormatter = new PowerStatsFormatter( extras.getString(EXTRA_UID_STATS_FORMAT)); } return mUidStatsFormatter; } /** * Returns the label associated with the give state key, e.g. "5G-high" for the * state of Mobile Radio representing the 5G mode and high signal power. Loading Loading @@ -491,20 +537,22 @@ public final class PowerStats { StringBuilder sb = new StringBuilder(); sb.append("duration=").append(durationMs).append(" ").append(descriptor.name); if (stats.length > 0) { sb.append("=").append(Arrays.toString(stats)); sb.append("=").append(descriptor.getDeviceStatsFormatter().format(stats)); } if (descriptor.stateStatsArrayLength != 0) { PowerStatsFormatter formatter = descriptor.getStateStatsFormatter(); for (int i = 0; i < stateStats.size(); i++) { sb.append(" ["); sb.append(" ("); sb.append(descriptor.getStateLabel(stateStats.keyAt(i))); sb.append("]="); sb.append(Arrays.toString(stateStats.valueAt(i))); sb.append(") "); sb.append(formatter.format(stateStats.valueAt(i))); } } PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter(); for (int i = 0; i < uidStats.size(); i++) { sb.append(uidPrefix) .append(UserHandle.formatUid(uidStats.keyAt(i))) .append(": ").append(Arrays.toString(uidStats.valueAt(i))); .append(": ").append(uidStatsFormatter.format(uidStats.valueAt(i))); } return sb.toString(); } Loading @@ -513,26 +561,29 @@ public final class PowerStats { * Prints the contents of the stats snapshot. */ public void dump(IndentingPrintWriter pw) { pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')'); pw.println(descriptor.name + " (" + descriptor.powerComponentId + ')'); pw.increaseIndent(); pw.print("duration", durationMs).println(); if (descriptor.statsArrayLength != 0) { pw.print("stats", Arrays.toString(stats)).println(); pw.println(descriptor.getDeviceStatsFormatter().format(stats)); } if (descriptor.stateStatsArrayLength != 0) { PowerStatsFormatter formatter = descriptor.getStateStatsFormatter(); for (int i = 0; i < stateStats.size(); i++) { pw.print("state "); pw.print(" ("); pw.print(descriptor.getStateLabel(stateStats.keyAt(i))); pw.print(": "); pw.print(Arrays.toString(stateStats.valueAt(i))); pw.print(") "); pw.print(formatter.format(stateStats.valueAt(i))); pw.println(); } } PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter(); for (int i = 0; i < uidStats.size(); i++) { pw.print("UID "); pw.print(uidStats.keyAt(i)); pw.print(UserHandle.formatUid(uidStats.keyAt(i))); pw.print(": "); pw.print(Arrays.toString(uidStats.valueAt(i))); pw.print(uidStatsFormatter.format(uidStats.valueAt(i))); pw.println(); } pw.decreaseIndent(); Loading @@ -542,4 +593,126 @@ public final class PowerStats { public String toString() { return "PowerStats: " + formatForBatteryHistory(" UID "); } public static class PowerStatsFormatter { private static class Section { public String label; public int position; public int length; public boolean optional; public boolean typePower; } private static final double NANO_TO_MILLI_MULTIPLIER = 1.0 / 1000000.0; private static final Pattern SECTION_PATTERN = Pattern.compile("([^:]+):(\\d+)(\\[(?<L>\\d+)])?(?<F>\\S*)\\s*"); private final List<Section> mSections; public PowerStatsFormatter(String format) { mSections = parseFormat(format); } /** * Produces a formatted string representing the supplied array, with labels * and other adornments specific to the power stats layout. */ public String format(long[] stats) { return format(mSections, stats); } private List<Section> parseFormat(String format) { if (format == null || format.isBlank()) { return null; } ArrayList<Section> sections = new ArrayList<>(); Matcher matcher = SECTION_PATTERN.matcher(format); for (int position = 0; position < format.length(); position = matcher.end()) { if (!matcher.find() || matcher.start() != position) { Slog.wtf(TAG, "Bad power stats format '" + format + "'"); return null; } Section section = new Section(); section.label = matcher.group(1); section.position = Integer.parseUnsignedInt(matcher.group(2)); String length = matcher.group("L"); if (length != null) { section.length = Integer.parseUnsignedInt(length); } else { section.length = 1; } String flags = matcher.group("F"); if (flags != null) { for (int i = 0; i < flags.length(); i++) { char flag = flags.charAt(i); switch (flag) { case '?': section.optional = true; break; case 'p': section.typePower = true; break; default: Slog.e(TAG, "Unsupported format option '" + flag + "' in " + format); break; } } } sections.add(section); } return sections; } private String format(List<Section> sections, long[] stats) { if (sections == null) { return Arrays.toString(stats); } StringBuilder sb = new StringBuilder(); for (int i = 0, count = sections.size(); i < count; i++) { Section section = sections.get(i); if (section.length == 0) { continue; } if (section.optional) { boolean nonZero = false; for (int offset = 0; offset < section.length; offset++) { if (stats[section.position + offset] != 0) { nonZero = true; break; } } if (!nonZero) { continue; } } if (!sb.isEmpty()) { sb.append(' '); } sb.append(section.label).append(": "); if (section.length != 1) { sb.append('['); } for (int offset = 0; offset < section.length; offset++) { if (offset != 0) { sb.append(", "); } if (section.typePower) { sb.append(BatteryStats.formatCharge( stats[section.position + offset] * NANO_TO_MILLI_MULTIPLIER)); } else { sb.append(stats[section.position + offset]); } } if (section.length != 1) { sb.append(']'); } } return sb.toString(); } } }
core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java +71 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.os.BatteryConsumer; import android.os.Parcel; import android.os.PersistableBundle; import android.platform.test.ravenwood.RavenwoodRule; import android.util.IndentingPrintWriter; import android.util.SparseArray; import android.util.Xml; Loading @@ -31,6 +32,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.google.common.truth.StringSubject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -38,6 +41,7 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.StringWriter; import java.nio.charset.StandardCharsets; @RunWith(AndroidJUnit4.class) Loading @@ -56,6 +60,9 @@ public class PowerStatsTest { extras.putBoolean("hasPowerMonitor", true); SparseArray<String> stateLabels = new SparseArray<>(); stateLabels.put(0x0F, "idle"); extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, "device:0[3]"); extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, "state:0"); extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, "a:0 b:1"); mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, stateLabels, 1, 2, extras); mRegistry.register(mDescriptor); Loading Loading @@ -191,4 +198,68 @@ public class PowerStatsTest { newParcel.setDataPosition(0); return newParcel; } @Test public void formatForBatteryHistory() { PowerStats stats = new PowerStats(mDescriptor); stats.durationMs = 1234; stats.stats[0] = 10; stats.stats[1] = 20; stats.stats[2] = 30; stats.stateStats.put(0x0F, new long[]{16}); stats.stateStats.put(0xF0, new long[]{17}); stats.uidStats.put(42, new long[]{40, 50}); stats.uidStats.put(99, new long[]{60, 70}); assertThat(stats.formatForBatteryHistory(" #")) .isEqualTo("duration=1234 cpu=" + "device: [10, 20, 30]" + " (idle) state: 16" + " (cpu-f0) state: 17" + " #42: a: 40 b: 50" + " #99: a: 60 b: 70"); } @Test public void dump() { PowerStats stats = new PowerStats(mDescriptor); stats.durationMs = 1234; stats.stats[0] = 10; stats.stats[1] = 20; stats.stats[2] = 30; stats.stateStats.put(0x0F, new long[]{16}); stats.stateStats.put(0xF0, new long[]{17}); stats.uidStats.put(42, new long[]{40, 50}); stats.uidStats.put(99, new long[]{60, 70}); StringWriter sw = new StringWriter(); IndentingPrintWriter pw = new IndentingPrintWriter(sw); stats.dump(pw); pw.flush(); String dump = sw.toString(); assertThat(dump).contains("duration=1234"); assertThat(dump).contains("device: [10, 20, 30]"); assertThat(dump).contains("(idle) state: 16"); assertThat(dump).contains("(cpu-f0) state: 17"); assertThat(dump).contains("UID 42: a: 40 b: 50"); assertThat(dump).contains("UID 99: a: 60 b: 70"); } @Test public void formatter() { assertThatFormatted(new long[]{12, 34, 56}, "a:0 b:1[2]") .isEqualTo("a: 12 b: [34, 56]"); assertThatFormatted(new long[]{12, 0, 0}, "a:0? b:1[2]?") .isEqualTo("a: 12"); assertThatFormatted(new long[]{0, 34, 56}, "a:0? b:1[2]?") .isEqualTo("b: [34, 56]"); assertThatFormatted(new long[]{3141592, 2000000, 1414213}, "pi:0p sqrt:1[2]p") .isEqualTo("pi: 3.14 sqrt: [2.00, 1.41]"); } private static StringSubject assertThatFormatted(long[] stats, String format) { return assertThat(new PowerStats.PowerStatsFormatter(format) .format(stats)); } }
services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java +5 −24 Original line number Diff line number Diff line Loading @@ -19,12 +19,9 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.os.BatteryConsumer; import com.android.internal.os.PowerStats; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** Loading Loading @@ -206,25 +203,9 @@ public class AggregatedPowerStatsConfig { return mPowerComponents; } private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() { private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() { @Override void finish(PowerComponentAggregatedPowerStats stats) { } @Override String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { return Arrays.toString(stats); } @Override String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { return descriptor.getStateLabel(key) + " " + Arrays.toString(stats); } @Override String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { return Arrays.toString(stats); } }; }
services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java +3 −3 Original line number Diff line number Diff line Loading @@ -44,7 +44,7 @@ public class CpuPowerStatsLayout extends PowerStatsLayout { * Declare that the stats array has a section capturing CPU time per scaling step */ public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) { mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount); mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount, "steps"); mDeviceCpuTimeByScalingStepCount = scalingStepCount; } Loading Loading @@ -72,7 +72,7 @@ public class CpuPowerStatsLayout extends PowerStatsLayout { * Declare that the stats array has a section capturing CPU time in each cluster */ public void addDeviceSectionCpuTimeByCluster(int clusterCount) { mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount); mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount, "clusters"); mDeviceCpuTimeByClusterCount = clusterCount; } Loading Loading @@ -102,7 +102,7 @@ public class CpuPowerStatsLayout extends PowerStatsLayout { public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) { mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap; updatePowerBracketCount(); mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount); mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount, "time"); } private void updatePowerBracketCount() { Loading
services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java +0 −61 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.power.stats; import android.os.BatteryStats; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -487,64 +486,4 @@ public class CpuPowerStatsProcessor extends PowerStatsProcessor { stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); } } @Override public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { unpackPowerStatsDescriptor(descriptor); StringBuilder sb = new StringBuilder(); int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount(); sb.append("steps: ["); for (int step = 0; step < cpuScalingStepCount; step++) { if (step != 0) { sb.append(", "); } sb.append(mStatsLayout.getTimeByScalingStep(stats, step)); } int clusterCount = mStatsLayout.getCpuClusterCount(); sb.append("] clusters: ["); for (int cluster = 0; cluster < clusterCount; cluster++) { if (cluster != 0) { sb.append(", "); } sb.append(mStatsLayout.getTimeByCluster(stats, cluster)); } sb.append("] uptime: ").append(mStatsLayout.getUsageDuration(stats)); int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); if (energyConsumerCount > 0) { sb.append(" energy: ["); for (int i = 0; i < energyConsumerCount; i++) { if (i != 0) { sb.append(", "); } sb.append(mStatsLayout.getConsumedEnergy(stats, i)); } sb.append("]"); } sb.append(" power: ").append( BatteryStats.formatCharge(mStatsLayout.getDevicePowerEstimate(stats))); return sb.toString(); } @Override String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { // Unsupported for this power component return null; } @Override public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { unpackPowerStatsDescriptor(descriptor); StringBuilder sb = new StringBuilder(); sb.append("["); int powerBracketCount = mStatsLayout.getCpuPowerBracketCount(); for (int bracket = 0; bracket < powerBracketCount; bracket++) { if (bracket != 0) { sb.append(", "); } sb.append(mStatsLayout.getUidTimeByPowerBracket(stats, bracket)); } sb.append("] power: ").append( BatteryStats.formatCharge(mStatsLayout.getUidPowerEstimate(stats))); return sb.toString(); } }