diff options
20 files changed, 593 insertions, 349 deletions
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java index 9f9aae53e0af..24971f51aabf 100644 --- a/core/java/com/android/internal/os/PowerStats.java +++ b/core/java/com/android/internal/os/PowerStats.java @@ -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; @@ -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 @@ -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"; @@ -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, @@ -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); @@ -154,6 +167,39 @@ public final class PowerStats { } /** + * 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. */ @@ -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(); } @@ -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(); @@ -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(); + } + } } diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java index baab3b218746..4846ed27af22 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java @@ -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; @@ -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; @@ -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) @@ -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); @@ -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)); + } } diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java index 5aad570ffd41..884c26ca3c00 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java @@ -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; /** @@ -206,25 +203,9 @@ public class AggregatedPowerStatsConfig { return mPowerComponents; } - 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); - } - }; + private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() { + @Override + void finish(PowerComponentAggregatedPowerStats stats) { + } + }; } diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java index 1bcb2c4bc5fa..2a02bd0f9e6a 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java @@ -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; } @@ -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; } @@ -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() { diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java index c34b8a8dc992..57b7259f9b56 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java @@ -16,7 +16,6 @@ package com.android.server.power.stats; -import android.os.BatteryStats; import android.util.ArraySet; import android.util.Log; @@ -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(); - } } diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java index 81d7c2fa2880..07d78f8ce4d0 100644 --- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java @@ -64,29 +64,30 @@ class MobileRadioPowerStatsLayout extends PowerStatsLayout { } void addDeviceMobileActivity() { - mDeviceSleepTimePosition = addDeviceSection(1); - mDeviceIdleTimePosition = addDeviceSection(1); - mDeviceScanTimePosition = addDeviceSection(1); - mDeviceCallTimePosition = addDeviceSection(1); + mDeviceSleepTimePosition = addDeviceSection(1, "sleep"); + mDeviceIdleTimePosition = addDeviceSection(1, "idle"); + mDeviceScanTimePosition = addDeviceSection(1, "scan"); + mDeviceCallTimePosition = addDeviceSection(1, "call", FLAG_OPTIONAL); } void addStateStats() { - mStateRxTimePosition = addStateSection(1); + mStateRxTimePosition = addStateSection(1, "rx"); mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels(); - mStateTxTimesPosition = addStateSection(mStateTxTimesCount); + mStateTxTimesPosition = addStateSection(mStateTxTimesCount, "tx"); } void addUidNetworkStats() { - mUidRxBytesPosition = addUidSection(1); - mUidTxBytesPosition = addUidSection(1); - mUidRxPacketsPosition = addUidSection(1); - mUidTxPacketsPosition = addUidSection(1); + mUidRxPacketsPosition = addUidSection(1, "rx-pkts"); + mUidRxBytesPosition = addUidSection(1, "rx-B"); + mUidTxPacketsPosition = addUidSection(1, "tx-pkts"); + mUidTxBytesPosition = addUidSection(1, "tx-B"); } @Override public void addDeviceSectionPowerEstimate() { super.addDeviceSectionPowerEstimate(); - mDeviceCallPowerPosition = addDeviceSection(1); + // Printed as part of the PhoneCallPowerStatsProcessor + mDeviceCallPowerPosition = addDeviceSection(1, "call-power", FLAG_HIDDEN); } public void setDeviceSleepTime(long[] stats, long durationMillis) { diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java index c97c64bafcba..eebed2f21946 100644 --- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java @@ -398,37 +398,4 @@ public class MobileRadioPowerStatsProcessor extends PowerStatsProcessor { } } } - - @Override - String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { - unpackPowerStatsDescriptor(descriptor); - return "idle: " + mStatsLayout.getDeviceIdleTime(stats) - + " sleep: " + mStatsLayout.getDeviceSleepTime(stats) - + " scan: " + mStatsLayout.getDeviceScanTime(stats) - + " power: " + mStatsLayout.getDevicePowerEstimate(stats); - } - - @Override - String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { - unpackPowerStatsDescriptor(descriptor); - StringBuilder sb = new StringBuilder(); - sb.append(descriptor.getStateLabel(key)); - sb.append(" rx: ").append(mStatsLayout.getStateRxTime(stats)); - sb.append(" tx: "); - for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) { - if (txLevel != 0) { - sb.append(", "); - } - sb.append(mStatsLayout.getStateTxTime(stats, txLevel)); - } - return sb.toString(); - } - - @Override - String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { - unpackPowerStatsDescriptor(descriptor); - return "rx: " + mStatsLayout.getUidRxPackets(stats) - + " tx: " + mStatsLayout.getUidTxPackets(stats) - + " power: " + mStatsLayout.getUidPowerEstimate(stats); - } } diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java index 6c4a2b6e6359..a8222811c341 100644 --- a/services/core/java/com/android/server/power/stats/MultiStateStats.java +++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java @@ -28,10 +28,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; -import java.io.PrintWriter; import java.util.Arrays; import java.util.function.Consumer; -import java.util.function.Function; /** * Maintains multidimensional multi-state stats. States could be something like on-battery (0,1), @@ -287,6 +285,14 @@ public class MultiStateStats { mCounter = new LongArrayMultiStateCounter(factory.mSerialStateCount, dimensionCount); } + public int getDimensionCount() { + return mFactory.mDimensionCount; + } + + public States[] getStates() { + return mFactory.mStates; + } + /** * Copies time-in-state and timestamps from the supplied prototype. Does not * copy accumulated counts. @@ -343,11 +349,6 @@ public class MultiStateStats { mTracking = false; } - @Override - public String toString() { - return mCounter.toString(); - } - /** * Stores contents in an XML doc. */ @@ -451,10 +452,9 @@ public class MultiStateStats { return true; } - /** - * Prints the accumulated stats, one line of every combination of states that has data. - */ - public void dump(PrintWriter pw, Function<long[], String> statsFormatter) { + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); long[] values = new long[mCounter.getArrayLength()]; States.forEachTrackedStateCombination(mFactory.mStates, states -> { mCounter.getCounts(values, mFactory.getSerialState(states)); @@ -469,18 +469,24 @@ public class MultiStateStats { return; } - StringBuilder sb = new StringBuilder(); + if (!sb.isEmpty()) { + sb.append("\n"); + } + + sb.append("("); + boolean first = true; for (int i = 0; i < states.length; i++) { if (mFactory.mStates[i].mTracked) { - if (sb.length() != 0) { + if (!first) { sb.append(" "); } + first = false; sb.append(mFactory.mStates[i].mLabels[states[i]]); } } - sb.append(" "); - sb.append(statsFormatter.apply(values)); - pw.println(sb); + sb.append(") "); + sb.append(Arrays.toString(values)); }); + return sb.toString(); } } diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java index 62b653f61373..5c545fd073b2 100644 --- a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java @@ -76,21 +76,4 @@ public class PhoneCallPowerStatsProcessor extends PowerStatsProcessor { stats.setDeviceStats(states, mTmpDeviceStats); }); } - - @Override - String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { - return "power: " + mStatsLayout.getDevicePowerEstimate(stats); - } - - @Override - String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { - // Unsupported for this power component - return null; - } - - @Override - String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { - // Unsupported for this power component - return null; - } } diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java index 6d58307dbefa..052873312d5c 100644 --- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java @@ -436,36 +436,76 @@ class PowerComponentAggregatedPowerStats { void dumpDevice(IndentingPrintWriter ipw) { if (mDeviceStats != null) { - ipw.println(mPowerStatsDescriptor.name); - ipw.increaseIndent(); - mDeviceStats.dump(ipw, stats -> - mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats)); - ipw.decreaseIndent(); + dumpMultiStateStats(ipw, mDeviceStats, mPowerStatsDescriptor.name, null, + mPowerStatsDescriptor.getDeviceStatsFormatter()); } if (mStateStats.size() != 0) { ipw.increaseIndent(); - ipw.println(mPowerStatsDescriptor.name + " states"); - ipw.increaseIndent(); + String header = mPowerStatsDescriptor.name + " states"; + PowerStats.PowerStatsFormatter formatter = + mPowerStatsDescriptor.getStateStatsFormatter(); for (int i = 0; i < mStateStats.size(); i++) { int key = mStateStats.keyAt(i); + String stateLabel = mPowerStatsDescriptor.getStateLabel(key); MultiStateStats stateStats = mStateStats.valueAt(i); - stateStats.dump(ipw, stats -> - mConfig.getProcessor().stateStatsToString(mPowerStatsDescriptor, key, - stats)); + dumpMultiStateStats(ipw, stateStats, header, stateLabel, formatter); } ipw.decreaseIndent(); - ipw.decreaseIndent(); } } void dumpUid(IndentingPrintWriter ipw, int uid) { UidStats uidStats = mUidStats.get(uid); if (uidStats != null && uidStats.stats != null) { - ipw.println(mPowerStatsDescriptor.name); - ipw.increaseIndent(); - uidStats.stats.dump(ipw, stats -> - mConfig.getProcessor().uidStatsToString(mPowerStatsDescriptor, stats)); + dumpMultiStateStats(ipw, uidStats.stats, mPowerStatsDescriptor.name, null, + mPowerStatsDescriptor.getUidStatsFormatter()); + } + } + + private void dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats, + String header, String additionalLabel, + PowerStats.PowerStatsFormatter statsFormatter) { + boolean[] firstLine = new boolean[]{true}; + long[] values = new long[stats.getDimensionCount()]; + MultiStateStats.States[] stateInfo = stats.getStates(); + MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> { + stats.getStats(values, states); + boolean nonZero = false; + for (long value : values) { + if (value != 0) { + nonZero = true; + break; + } + } + if (!nonZero) { + return; + } + + if (firstLine[0]) { + ipw.println(header); + ipw.increaseIndent(); + } + firstLine[0] = false; + StringBuilder sb = new StringBuilder(); + sb.append("("); + boolean first = true; + for (int i = 0; i < states.length; i++) { + if (stateInfo[i].isTracked()) { + if (!first) { + sb.append(" "); + } + first = false; + sb.append(stateInfo[i].getLabels()[states[i]]); + } + } + if (additionalLabel != null) { + sb.append(" ").append(additionalLabel); + } + sb.append(") ").append(statsFormatter.format(values)); + ipw.println(sb); + }); + if (!firstLine[0]) { ipw.decreaseIndent(); } } diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java index aa96409e85e9..58efd94bb82c 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java @@ -33,13 +33,20 @@ public class PowerStatsLayout { private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec"; private static final String EXTRA_UID_POWER_POSITION = "up"; - protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0; protected static final int UNSUPPORTED = -1; + protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0; + protected static final int FLAG_OPTIONAL = 1; + protected static final int FLAG_HIDDEN = 2; + protected static final int FLAG_FORMAT_AS_POWER = 4; private int mDeviceStatsArrayLength; private int mStateStatsArrayLength; private int mUidStatsArrayLength; + private StringBuilder mDeviceFormat = new StringBuilder(); + private StringBuilder mStateFormat = new StringBuilder(); + private StringBuilder mUidFormat = new StringBuilder(); + protected int mDeviceDurationPosition = UNSUPPORTED; private int mDeviceEnergyConsumerPosition; private int mDeviceEnergyConsumerCount; @@ -65,29 +72,71 @@ public class PowerStatsLayout { return mUidStatsArrayLength; } - protected int addDeviceSection(int length) { + /** + * @param label should not contain either spaces or colons + */ + private void appendFormat(StringBuilder sb, int position, int length, String label, + int flags) { + if ((flags & FLAG_HIDDEN) != 0) { + return; + } + + if (!sb.isEmpty()) { + sb.append(' '); + } + + sb.append(label).append(':'); + sb.append(position); + if (length != 1) { + sb.append('[').append(length).append(']'); + } + if ((flags & FLAG_FORMAT_AS_POWER) != 0) { + sb.append('p'); + } + if ((flags & FLAG_OPTIONAL) != 0) { + sb.append('?'); + } + } + + protected int addDeviceSection(int length, String label, int flags) { int position = mDeviceStatsArrayLength; mDeviceStatsArrayLength += length; + appendFormat(mDeviceFormat, position, length, label, flags); return position; } - protected int addStateSection(int length) { + protected int addDeviceSection(int length, String label) { + return addDeviceSection(length, label, 0); + } + + protected int addStateSection(int length, String label, int flags) { int position = mStateStatsArrayLength; mStateStatsArrayLength += length; + appendFormat(mStateFormat, position, length, label, flags); return position; } - protected int addUidSection(int length) { + protected int addStateSection(int length, String label) { + return addStateSection(length, label, 0); + } + + + protected int addUidSection(int length, String label, int flags) { int position = mUidStatsArrayLength; mUidStatsArrayLength += length; + appendFormat(mUidFormat, position, length, label, flags); return position; } + protected int addUidSection(int length, String label) { + return addUidSection(length, label, 0); + } + /** * Declare that the stats array has a section capturing usage duration */ public void addDeviceSectionUsageDuration() { - mDeviceDurationPosition = addDeviceSection(1); + mDeviceDurationPosition = addDeviceSection(1, "usage", FLAG_OPTIONAL); } /** @@ -109,7 +158,7 @@ public class PowerStatsLayout { * PowerStatsService. */ public void addDeviceSectionEnergyConsumers(int energyConsumerCount) { - mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount); + mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy"); mDeviceEnergyConsumerCount = energyConsumerCount; } @@ -137,7 +186,8 @@ public class PowerStatsLayout { * Declare that the stats array has a section capturing a power estimate */ public void addDeviceSectionPowerEstimate() { - mDevicePowerEstimatePosition = addDeviceSection(1); + mDevicePowerEstimatePosition = addDeviceSection(1, "power", + FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL); } /** @@ -159,7 +209,7 @@ public class PowerStatsLayout { * Declare that the UID stats array has a section capturing a power estimate */ public void addUidSectionPowerEstimate() { - mUidPowerEstimatePosition = addUidSection(1); + mUidPowerEstimatePosition = addUidSection(1, "power", FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL); } /** @@ -195,6 +245,9 @@ public class PowerStatsLayout { mDeviceEnergyConsumerCount); extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition); extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition); + extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString()); + extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString()); + extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString()); } /** diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java index 0d5c5422b45c..2fd0b9a9b001 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java @@ -19,8 +19,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; -import com.android.internal.os.PowerStats; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -47,12 +45,6 @@ abstract class PowerStatsProcessor { abstract void finish(PowerComponentAggregatedPowerStats stats); - abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats); - - abstract String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats); - - abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats); - protected static class PowerEstimationPlan { private final AggregatedPowerStatsConfig.PowerComponent mConfig; public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>(); diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java index 0fa6ec65c4bc..e2e822690c55 100644 --- a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java +++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java @@ -65,28 +65,28 @@ public class WifiPowerStatsLayout extends PowerStatsLayout { mPowerReportingSupported = powerReportingSupported; if (mPowerReportingSupported) { mDeviceActiveTimePosition = UNSPECIFIED; - mDeviceRxTimePosition = addDeviceSection(1); - mDeviceTxTimePosition = addDeviceSection(1); - mDeviceIdleTimePosition = addDeviceSection(1); - mDeviceScanTimePosition = addDeviceSection(1); + mDeviceRxTimePosition = addDeviceSection(1, "rx"); + mDeviceTxTimePosition = addDeviceSection(1, "tx"); + mDeviceIdleTimePosition = addDeviceSection(1, "idle"); + mDeviceScanTimePosition = addDeviceSection(1, "scan"); } else { - mDeviceActiveTimePosition = addDeviceSection(1); + mDeviceActiveTimePosition = addDeviceSection(1, "rx-tx"); mDeviceRxTimePosition = UNSPECIFIED; mDeviceTxTimePosition = UNSPECIFIED; mDeviceIdleTimePosition = UNSPECIFIED; mDeviceScanTimePosition = UNSPECIFIED; } - mDeviceBasicScanTimePosition = addDeviceSection(1); - mDeviceBatchedScanTimePosition = addDeviceSection(1); + mDeviceBasicScanTimePosition = addDeviceSection(1, "basic-scan", FLAG_OPTIONAL); + mDeviceBatchedScanTimePosition = addDeviceSection(1, "batched-scan", FLAG_OPTIONAL); } void addUidNetworkStats() { - mUidRxBytesPosition = addUidSection(1); - mUidTxBytesPosition = addUidSection(1); - mUidRxPacketsPosition = addUidSection(1); - mUidTxPacketsPosition = addUidSection(1); - mUidScanTimePosition = addUidSection(1); - mUidBatchScanTimePosition = addUidSection(1); + mUidRxPacketsPosition = addUidSection(1, "rx-pkts"); + mUidRxBytesPosition = addUidSection(1, "rx-B"); + mUidTxPacketsPosition = addUidSection(1, "tx-pkts"); + mUidTxBytesPosition = addUidSection(1, "tx-B"); + mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL); + mUidBatchScanTimePosition = addUidSection(1, "batched-scan", FLAG_OPTIONAL); } public boolean isPowerReportingSupported() { diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java index 5e9cc4092029..a4a2e183f86b 100644 --- a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java @@ -389,37 +389,4 @@ public class WifiPowerStatsProcessor extends PowerStatsProcessor { } } } - - @Override - String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { - unpackPowerStatsDescriptor(descriptor); - if (mHasWifiPowerController) { - return "rx: " + mStatsLayout.getDeviceRxTime(stats) - + " tx: " + mStatsLayout.getDeviceTxTime(stats) - + " scan: " + mStatsLayout.getDeviceScanTime(stats) - + " idle: " + mStatsLayout.getDeviceIdleTime(stats) - + " power: " + mStatsLayout.getDevicePowerEstimate(stats); - } else { - return "active: " + mStatsLayout.getDeviceActiveTime(stats) - + " scan: " + mStatsLayout.getDeviceBasicScanTime(stats) - + " batched-scan: " + mStatsLayout.getDeviceBatchedScanTime(stats) - + " power: " + mStatsLayout.getDevicePowerEstimate(stats); - } - } - - @Override - String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { - // Unsupported for this power component - return null; - } - - @Override - String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { - unpackPowerStatsDescriptor(descriptor); - return "rx: " + mStatsLayout.getUidRxPackets(stats) - + " tx: " + mStatsLayout.getUidTxPackets(stats) - + " scan: " + mStatsLayout.getUidScanTime(stats) - + " batched-scan: " + mStatsLayout.getUidBatchedScanTime(stats) - + " power: " + mStatsLayout.getUidPowerEstimate(stats); - } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java index 4e3e80f8ff10..b5bdafeb4696 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java @@ -32,6 +32,7 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.platform.test.ravenwood.RavenwoodRule; +import android.util.IndentingPrintWriter; import android.util.SparseArray; import androidx.test.filters.SmallTest; @@ -51,6 +52,7 @@ import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.StringWriter; import java.util.function.IntSupplier; @RunWith(AndroidJUnit4.class) @@ -363,6 +365,36 @@ public class CpuPowerStatsCollectorTest { .isEqualTo(528); } + @Test + public void dump() { + mockCpuScalingPolicies(1); + mockPowerProfile(); + mockEnergyConsumers(); + + CpuPowerStatsCollector collector = createCollector(8, 0); + collector.collectStats(); // Establish baseline + + mockKernelCpuStats(new long[]{1111, 2222, 3333}, + new SparseArray<>() {{ + put(UID_1, new long[]{100, 200}); + put(UID_2, new long[]{100, 150}); + put(ISOLATED_UID, new long[]{200, 450}); + }}, 0, 1234); + + PowerStats powerStats = collector.collectStats(); + + StringWriter sw = new StringWriter(); + IndentingPrintWriter pw = new IndentingPrintWriter(sw); + powerStats.dump(pw); + pw.flush(); + String dump = sw.toString(); + + assertThat(dump).contains("duration=1234"); + assertThat(dump).contains("steps: [1111, 2222, 3333]"); + assertThat(dump).contains("UID 42: time: [100, 200]"); + assertThat(dump).contains("UID 99: time: [300, 600]"); + } + private void mockCpuScalingPolicies(int clusterCount) { SparseArray<int[]> cpus = new SparseArray<>(); SparseArray<int[]> freqs = new SparseArray<>(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java index 70c40f5052f0..644ae4717eb1 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; +import android.os.UserHandle; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -46,7 +47,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; @@ -92,9 +95,10 @@ public class CpuPowerStatsCollectorValidationTest { long duration = 0; long[] stats = null; - String[] cpuStatsDump = dumpCpuStats(); + List<String> cpuStatsDump = dumpCpuStats(); Pattern durationPattern = Pattern.compile("duration=([0-9]*)"); - Pattern uidPattern = Pattern.compile("UID " + mTestPkgUid + ": \\[([0-9,\\s]*)]"); + Pattern uidPattern = Pattern.compile( + "UID " + UserHandle.formatUid(mTestPkgUid) + ": time: [\\[]?([0-9,\\s]*)[]]?"); for (String line : cpuStatsDump) { Matcher durationMatcher = durationPattern.matcher(line); if (durationMatcher.find()) { @@ -119,15 +123,23 @@ public class CpuPowerStatsCollectorValidationTest { assertThat(total).isAtLeast((long) (WORK_DURATION_MS * 0.8)); } - private String[] dumpCpuStats() throws Exception { + private List<String> dumpCpuStats() throws Exception { + ArrayList<String> cpuStats = new ArrayList<>(); String dump = executeCmdSilent("dumpsys batterystats --sample"); String[] lines = dump.split("\n"); + boolean inCpuSection = false; for (int i = 0; i < lines.length; i++) { - if (lines[i].startsWith("CpuPowerStatsCollector")) { - return Arrays.copyOfRange(lines, i + 1, lines.length); + if (!inCpuSection) { + if (lines[i].startsWith("CpuPowerStatsCollector")) { + inCpuSection = true; + } + } else if (lines[i].startsWith(" ")) { + cpuStats.add(lines[i]); + } else { + break; } } - return new String[0]; + return cpuStats; } private void doSomeWork() throws Exception { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java index f93c4da3d8d0..ca58db1bcad5 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java @@ -337,12 +337,14 @@ public class MobileRadioPowerStatsCollectorTest { pw.flush(); String dump = sw.toString(); assertThat(dump).contains("duration=100"); + assertThat(dump).contains("sleep: 200 idle: 300 scan: 60000 call: 40000 energy: " + + ((64321 - 10000) * 1000 / 3500)); + assertThat(dump).contains("(LTE) rx: 7000 tx: [8000, 9000, 1000, 2000, 3000]"); + assertThat(dump).contains("(NR MMWAVE) rx: 6000 tx: [1000, 2000, 3000, 4000, 5000]"); assertThat(dump).contains( - "stats=[200, 300, 60000, 40000, " + ((64321 - 10000) * 1000 / 3500) + ", 0, 0, 0]"); - assertThat(dump).contains("state LTE: [7000, 8000, 9000, 1000, 2000, 3000]"); - assertThat(dump).contains("state NR MMWAVE: [6000, 1000, 2000, 3000, 4000, 5000]"); - assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 0]"); - assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 0]"); + "UID 24: rx-pkts: 60 rx-B: 6000 tx-pkts: 30 tx-B: 3000"); + assertThat(dump).contains( + "UID 42: rx-pkts: 100 rx-B: 1000 tx-pkts: 200 tx-B: 2000"); } private PowerStats collectPowerStats(boolean perNetworkTypeData) throws Throwable { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java index 4ac7ad8d07ff..d80ffb4a66c8 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java @@ -305,7 +305,100 @@ public class MobileRadioPowerStatsProcessorTest { } @Test - public void measuredEnergyModel() { + public void energyConsumerModel() { + PowerComponentAggregatedPowerStats aggregatedStats = + prepareAggregatedStats_energyConsumerModel(); + + MobileRadioPowerStatsLayout statsLayout = + new MobileRadioPowerStatsLayout( + aggregatedStats.getPowerStatsDescriptor()); + + // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh + double totalPower = 0; + long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength]; + aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.671837); + totalPower += statsLayout.getDevicePowerEstimate(deviceStats); + assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.022494); + totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats); + + aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(2.01596); + totalPower += statsLayout.getDevicePowerEstimate(deviceStats); + assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.067484); + totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats); + + // These estimates are supposed to add up to the measured energy, 2.77778 mAh + assertThat(totalPower).isWithin(PRECISION).of(2.77778); + + double uidPower1 = 0; + long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength]; + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.198236); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.198236); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.396473); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + double uidPower2 = 0; + aggregatedStats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.066078); + uidPower2 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.198236); + uidPower2 += statsLayout.getUidPowerEstimate(uidStats); + + // Total power attributed to apps is significantly less than the grand total, + // because we only attribute TX/RX to apps but not maintaining a connection with the cell. + assertThat(uidPower1 + uidPower2) + .isWithin(PRECISION).of(1.057259); + + // 3/4 of total packets were sent by APP_UID so 75% of total RX/TX power is attributed to it + assertThat(uidPower1 / (uidPower1 + uidPower2)) + .isWithin(PRECISION).of(0.75); + } + + @Test + public void test_toString() { + PowerComponentAggregatedPowerStats stats = prepareAggregatedStats_energyConsumerModel(); + String string = stats.toString(); + assertThat(string).contains("(pwr-other scr-on)" + + " sleep: 500 idle: 750 scan: 1388 call: 50 energy: 2500000 power: 0.672"); + assertThat(string).contains("(pwr-other scr-other)" + + " sleep: 1500 idle: 2250 scan: 4166 call: 150 energy: 7500000 power: 2.02"); + assertThat(string).contains("(pwr-other scr-on other)" + + " rx: 150 tx: [25, 50, 75, 100, 125]"); + assertThat(string).contains("(pwr-other scr-other other)" + + " rx: 450 tx: [75, 150, 225, 300, 375]"); + assertThat(string).contains("(pwr-other scr-on fg)" + + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198"); + assertThat(string).contains("(pwr-other scr-other bg)" + + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198"); + assertThat(string).contains("(pwr-other scr-other fgs)" + + " rx-pkts: 750 rx-B: 5000 tx-pkts: 150 tx-B: 10000 power: 0.396"); + } + + private PowerComponentAggregatedPowerStats prepareAggregatedStats_energyConsumerModel() { // PowerStats hardware is available when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO)) .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID}); @@ -378,74 +471,7 @@ public class MobileRadioPowerStatsProcessorTest { aggregatedStats.addPowerStats(powerStats, 10_000); processor.finish(aggregatedStats); - - MobileRadioPowerStatsLayout statsLayout = - new MobileRadioPowerStatsLayout( - aggregatedStats.getPowerStatsDescriptor()); - - // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh - double totalPower = 0; - long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength]; - aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); - assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) - .isWithin(PRECISION).of(0.671837); - totalPower += statsLayout.getDevicePowerEstimate(deviceStats); - assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats)) - .isWithin(PRECISION).of(0.022494); - totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats); - - aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); - assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) - .isWithin(PRECISION).of(2.01596); - totalPower += statsLayout.getDevicePowerEstimate(deviceStats); - assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats)) - .isWithin(PRECISION).of(0.067484); - totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats); - - // These estimates are supposed to add up to the measured energy, 2.77778 mAh - assertThat(totalPower).isWithin(PRECISION).of(2.77778); - - double uidPower1 = 0; - long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength]; - aggregatedStats.getUidStats(uidStats, APP_UID, - states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); - assertThat(statsLayout.getUidPowerEstimate(uidStats)) - .isWithin(PRECISION).of(0.198236); - uidPower1 += statsLayout.getUidPowerEstimate(uidStats); - - aggregatedStats.getUidStats(uidStats, APP_UID, - states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); - assertThat(statsLayout.getUidPowerEstimate(uidStats)) - .isWithin(PRECISION).of(0.198236); - uidPower1 += statsLayout.getUidPowerEstimate(uidStats); - - aggregatedStats.getUidStats(uidStats, APP_UID, - states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); - assertThat(statsLayout.getUidPowerEstimate(uidStats)) - .isWithin(PRECISION).of(0.396473); - uidPower1 += statsLayout.getUidPowerEstimate(uidStats); - - double uidPower2 = 0; - aggregatedStats.getUidStats(uidStats, APP_UID2, - states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); - assertThat(statsLayout.getUidPowerEstimate(uidStats)) - .isWithin(PRECISION).of(0.066078); - uidPower2 += statsLayout.getUidPowerEstimate(uidStats); - - aggregatedStats.getUidStats(uidStats, APP_UID2, - states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); - assertThat(statsLayout.getUidPowerEstimate(uidStats)) - .isWithin(PRECISION).of(0.198236); - uidPower2 += statsLayout.getUidPowerEstimate(uidStats); - - // Total power attributed to apps is significantly less than the grand total, - // because we only attribute TX/RX to apps but not maintaining a connection with the cell. - assertThat(uidPower1 + uidPower2) - .isWithin(PRECISION).of(1.057259); - - // 3/4 of total packets were sent by APP_UID so 75% of total RX/TX power is attributed to it - assertThat(uidPower1 / (uidPower1 + uidPower2)) - .isWithin(PRECISION).of(0.75); + return aggregatedStats; } private int[] states(int... states) { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java index 1b045c532759..ae258cd3c234 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java @@ -29,8 +29,6 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.Arrays; @RunWith(AndroidJUnit4.class) @@ -165,7 +163,7 @@ public class MultiStateStatsTest { } @Test - public void dump() { + public void test_toString() { MultiStateStats.Factory factory = makeFactory(true, true, false); MultiStateStats multiStateStats = factory.create(); multiStateStats.setState(0 /* batteryState */, 0 /* off */, 1000); @@ -175,13 +173,10 @@ public class MultiStateStatsTest { multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 3000); multiStateStats.increment(new long[]{100, 200}, 5000); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw, true); - multiStateStats.dump(pw, Arrays::toString); - assertThat(sw.toString()).isEqualTo( - "plugged-in fg [25, 50]\n" - + "on-battery fg [25, 50]\n" - + "on-battery bg [50, 100]\n" + assertThat(multiStateStats.toString()).isEqualTo( + "(plugged-in fg) [25, 50]\n" + + "(on-battery fg) [25, 50]\n" + + "(on-battery bg) [50, 100]" ); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java index 8b1d423abd21..b4c012b3460f 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java @@ -304,10 +304,14 @@ public class WifiPowerStatsCollectorTest { String dump = sw.toString(); assertThat(dump).contains("duration=7500"); assertThat(dump).contains( - "stats=[6000, 1000, 300, 200, 634, 945, " + ((64321 - 10000) * 1000 / 3500) - + ", 0, 0]"); - assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 400, 600, 0]"); - assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 234, 345, 0]"); + "rx: 6000 tx: 1000 idle: 300 scan: 200 basic-scan: 634 batched-scan: 945" + + " energy: " + ((64321 - 10000) * 1000 / 3500)); + assertThat(dump).contains( + "UID 24: rx-pkts: 60 rx-B: 6000 tx-pkts: 30 tx-B: 3000" + + " scan: 400 batched-scan: 600"); + assertThat(dump).contains( + "UID 42: rx-pkts: 100 rx-B: 1000 tx-pkts: 200 tx-B: 2000" + + " scan: 234 batched-scan: 345"); } private PowerStats collectPowerStats(boolean hasPowerReporting) { |