Loading core/java/com/android/internal/os/PowerStats.java +206 −4 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,13 @@ package com.android.internal.os; package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.BatteryConsumer; import android.os.BatteryConsumer; import android.os.Bundle; import android.os.Parcel; import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.SparseArray; import android.util.SparseArray; import java.util.Arrays; import java.util.Arrays; Loading @@ -27,10 +32,138 @@ import java.util.Arrays; * details. * details. */ */ public final class PowerStats { public final class PowerStats { private static final String TAG = "PowerStats"; private static final BatteryStatsHistory.VarintParceler VARINT_PARCELER = new BatteryStatsHistory.VarintParceler(); private static final byte PARCEL_FORMAT_VERSION = 1; private static final int PARCEL_FORMAT_VERSION_MASK = 0x000000FF; private static final int PARCEL_FORMAT_VERSION_SHIFT = Integer.numberOfTrailingZeros(PARCEL_FORMAT_VERSION_MASK); private static final int STATS_ARRAY_LENGTH_MASK = 0x0000FF00; private static final int STATS_ARRAY_LENGTH_SHIFT = Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK); public static final int MAX_STATS_ARRAY_LENGTH = 2 ^ Integer.bitCount(STATS_ARRAY_LENGTH_MASK) - 1; private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000; private static final int UID_STATS_ARRAY_LENGTH_SHIFT = Integer.numberOfTrailingZeros(UID_STATS_ARRAY_LENGTH_MASK); public static final int MAX_UID_STATS_ARRAY_LENGTH = (2 ^ Integer.bitCount(UID_STATS_ARRAY_LENGTH_MASK)) - 1; /** * Descriptor of the stats collected for a given power component (e.g. CPU, WiFi etc). * This descriptor is used for storing PowerStats and can also be used by power models * to adjust the algorithm in accordance with the stats available on the device. */ public static class Descriptor { /** * {@link BatteryConsumer.PowerComponent} (e.g. CPU, WIFI etc) that this snapshot relates * to; or a custom power component ID (if the value * is >= {@link BatteryConsumer#FIRST_CUSTOM_POWER_COMPONENT_ID}). */ public final int powerComponentId; public final String name; public final int statsArrayLength; public final int uidStatsArrayLength; /** * Extra parameters specific to the power component, e.g. the availability of power * monitors. */ public final Bundle extras; public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId, int statsArrayLength, int uidStatsArrayLength, @NonNull Bundle extras) { this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId), statsArrayLength, uidStatsArrayLength, extras); } public Descriptor(int customPowerComponentId, String name, int statsArrayLength, int uidStatsArrayLength, Bundle extras) { if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH); } if (uidStatsArrayLength > MAX_UID_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "uidStatsArrayLength is too high. Max = " + MAX_UID_STATS_ARRAY_LENGTH); } this.powerComponentId = customPowerComponentId; this.name = name; this.statsArrayLength = statsArrayLength; this.uidStatsArrayLength = uidStatsArrayLength; this.extras = extras; } /** * Writes the Descriptor into the parcel. */ public void writeSummaryToParcel(Parcel parcel) { int firstWord = ((PARCEL_FORMAT_VERSION << PARCEL_FORMAT_VERSION_SHIFT) & PARCEL_FORMAT_VERSION_MASK) | ((statsArrayLength << STATS_ARRAY_LENGTH_SHIFT) & STATS_ARRAY_LENGTH_MASK) | ((uidStatsArrayLength << UID_STATS_ARRAY_LENGTH_SHIFT) & UID_STATS_ARRAY_LENGTH_MASK); parcel.writeInt(firstWord); parcel.writeInt(powerComponentId); parcel.writeString(name); extras.writeToParcel(parcel, 0); } /** * Reads a Descriptor from the parcel. If the parcel has an incompatible format, * returns null. */ @Nullable public static Descriptor readSummaryFromParcel(Parcel parcel) { int firstWord = parcel.readInt(); int version = (firstWord & PARCEL_FORMAT_VERSION_MASK) >>> PARCEL_FORMAT_VERSION_SHIFT; if (version != PARCEL_FORMAT_VERSION) { Log.w(TAG, "Cannot read PowerStats from Parcel - the parcel format version has " + "changed from " + version + " to " + PARCEL_FORMAT_VERSION); return null; } int statsArrayLength = (firstWord & STATS_ARRAY_LENGTH_MASK) >>> STATS_ARRAY_LENGTH_SHIFT; int uidStatsArrayLength = (firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT; int powerComponentId = parcel.readInt(); String name = parcel.readString(); Bundle extras = parcel.readBundle(PowerStats.class.getClassLoader()); return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength, extras); } } /** * A registry for all supported power component types (e.g. CPU, WiFi). */ public static class DescriptorRegistry { private final SparseArray<Descriptor> mDescriptors = new SparseArray<>(); /** /** * Power component (e.g. CPU, WIFI etc) that this snapshot relates to. * Adds the specified descriptor to the registry. If the registry already * contained a descriptor for the same power component, then the new one replaces * the old one. */ */ public @BatteryConsumer.PowerComponent int powerComponentId; public void register(Descriptor descriptor) { mDescriptors.put(descriptor.powerComponentId, descriptor); } /** * @param powerComponentId either a BatteryConsumer.PowerComponent or a custom power * component ID */ public Descriptor get(int powerComponentId) { return mDescriptors.get(powerComponentId); } } public final Descriptor descriptor; /** /** * Duration, in milliseconds, covered by this snapshot. * Duration, in milliseconds, covered by this snapshot. Loading @@ -47,12 +180,81 @@ public final class PowerStats { */ */ public final SparseArray<long[]> uidStats = new SparseArray<>(); public final SparseArray<long[]> uidStats = new SparseArray<>(); public PowerStats(Descriptor descriptor) { this.descriptor = descriptor; stats = new long[descriptor.statsArrayLength]; } /** * Writes the object into the parcel. */ public void writeToParcel(Parcel parcel) { int lengthPos = parcel.dataPosition(); parcel.writeInt(0); // Placeholder for length int startPos = parcel.dataPosition(); parcel.writeInt(descriptor.powerComponentId); parcel.writeLong(durationMs); VARINT_PARCELER.writeLongArray(parcel, stats); parcel.writeInt(uidStats.size()); for (int i = 0; i < uidStats.size(); i++) { parcel.writeInt(uidStats.keyAt(i)); VARINT_PARCELER.writeLongArray(parcel, uidStats.valueAt(i)); } int endPos = parcel.dataPosition(); parcel.setDataPosition(lengthPos); parcel.writeInt(endPos - startPos); parcel.setDataPosition(endPos); } /** * Reads a PowerStats object from the supplied Parcel. If the parcel has an incompatible * format, returns null. */ @Nullable public static PowerStats readFromParcel(Parcel parcel, DescriptorRegistry registry) { int length = parcel.readInt(); int startPos = parcel.dataPosition(); int endPos = startPos + length; try { int powerComponentId = parcel.readInt(); Descriptor descriptor = registry.get(powerComponentId); if (descriptor == null) { Log.e(TAG, "Unsupported PowerStats for power component ID: " + powerComponentId); return null; } PowerStats stats = new PowerStats(descriptor); stats.durationMs = parcel.readLong(); stats.stats = new long[descriptor.statsArrayLength]; VARINT_PARCELER.readLongArray(parcel, stats.stats); int uidCount = parcel.readInt(); for (int i = 0; i < uidCount; i++) { int uid = parcel.readInt(); long[] uidStats = new long[descriptor.uidStatsArrayLength]; VARINT_PARCELER.readLongArray(parcel, uidStats); stats.uidStats.put(uid, uidStats); } if (parcel.dataPosition() != endPos) { Log.e(TAG, "Corrupted PowerStats parcel. Expected length: " + length + ", actual length: " + (parcel.dataPosition() - startPos)); return null; } return stats; } finally { // Unconditionally skip to the end of the written data, even if the actual parcel // format is incompatible parcel.setDataPosition(endPos); } } /** /** * Prints the contents of the stats snapshot. * Prints the contents of the stats snapshot. */ */ public void dump(IndentingPrintWriter pw) { public void dump(IndentingPrintWriter pw) { pw.print("PowerStats: "); pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')'); pw.println(BatteryConsumer.powerComponentIdToString(powerComponentId)); pw.increaseIndent(); pw.increaseIndent(); pw.print("duration", durationMs).println(); pw.print("duration", durationMs).println(); for (int i = 0; i < uidStats.size(); i++) { for (int i = 0; i < uidStats.size(); i++) { Loading core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +2 −1 Original line number Original line Diff line number Diff line Loading @@ -35,6 +35,7 @@ import org.junit.runners.Suite; LongArrayMultiStateCounterTest.class, LongArrayMultiStateCounterTest.class, LongMultiStateCounterTest.class, LongMultiStateCounterTest.class, PowerProfileTest.class, PowerProfileTest.class, PowerStatsTest.class, EnergyConsumerStatsTest.class EnergyConsumerStatsTest.class }) }) Loading core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java 0 → 100644 +132 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; import android.os.BatteryConsumer; import android.os.Bundle; import android.os.Parcel; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest public class PowerStatsTest { private PowerStats.DescriptorRegistry mRegistry; private PowerStats.Descriptor mDescriptor; @Before public void setup() { mRegistry = new PowerStats.DescriptorRegistry(); Bundle extras = new Bundle(); extras.putBoolean("hasPowerMonitor", true); mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras); mRegistry.register(mDescriptor); } @Test public void parceling_compatibleParcel() { PowerStats stats = new PowerStats(mDescriptor); stats.durationMs = 1234; stats.stats[0] = 10; stats.stats[1] = 20; stats.stats[2] = 30; stats.uidStats.put(42, new long[] {40, 50}); stats.uidStats.put(99, new long[] {60, 70}); Parcel parcel = Parcel.obtain(); mDescriptor.writeSummaryToParcel(parcel); stats.writeToParcel(parcel); parcel.writeString("END"); Parcel newParcel = marshallAndUnmarshall(parcel); PowerStats.Descriptor newDescriptor = PowerStats.Descriptor.readSummaryFromParcel(newParcel); assertThat(newDescriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU); assertThat(newDescriptor.name).isEqualTo("cpu"); assertThat(newDescriptor.statsArrayLength).isEqualTo(3); assertThat(newDescriptor.uidStatsArrayLength).isEqualTo(2); assertThat(newDescriptor.extras.getBoolean("hasPowerMonitor")).isTrue(); mRegistry.register(newDescriptor); PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry); assertThat(newStats.durationMs).isEqualTo(1234); assertThat(newStats.stats).isEqualTo(new long[] {10, 20, 30}); assertThat(newStats.uidStats.size()).isEqualTo(2); assertThat(newStats.uidStats.get(42)).isEqualTo(new long[] {40, 50}); assertThat(newStats.uidStats.get(99)).isEqualTo(new long[] {60, 70}); String end = newParcel.readString(); assertThat(end).isEqualTo("END"); } @Test public void parceling_unrecognizedPowerComponent() { PowerStats stats = new PowerStats( new PowerStats.Descriptor(777, "luck", 3, 2, new Bundle())); stats.durationMs = 1234; Parcel parcel = Parcel.obtain(); stats.writeToParcel(parcel); parcel.writeString("END"); Parcel newParcel = marshallAndUnmarshall(parcel); PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry); assertThat(newStats).isNull(); String end = newParcel.readString(); assertThat(end).isEqualTo("END"); } @Test public void parceling_wrongArrayLength() { PowerStats stats = new PowerStats(mDescriptor); stats.stats = new long[5]; // Is expected to be 3 Parcel parcel = Parcel.obtain(); stats.writeToParcel(parcel); parcel.writeString("END"); Parcel newParcel = marshallAndUnmarshall(parcel); PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry); assertThat(newStats).isNull(); String end = newParcel.readString(); assertThat(end).isEqualTo("END"); } private static Parcel marshallAndUnmarshall(Parcel parcel) { byte[] bytes = parcel.marshall(); parcel.recycle(); Parcel newParcel = Parcel.obtain(); newParcel.unmarshall(bytes, 0, bytes.length); newParcel.setDataPosition(0); return newParcel; } } services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +7 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.power.stats; package com.android.server.power.stats; import android.os.BatteryConsumer; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.util.SparseArray; import android.util.SparseArray; Loading @@ -42,7 +44,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private final int mUidStatsSize; private final int mUidStatsSize; // Reusable instance // Reusable instance private final PowerStats mCpuPowerStats = new PowerStats(); private final PowerStats mCpuPowerStats; private long mLastUpdateTimestampNanos; private long mLastUpdateTimestampNanos; public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, Loading @@ -69,6 +71,10 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { } } mUidStatsSize = powerProfile.getCpuPowerBracketCount(); mUidStatsSize = powerProfile.getCpuPowerBracketCount(); mTempUidStats = new long[mUidStatsSize]; mTempUidStats = new long[mUidStatsSize]; mCpuPowerStats = new PowerStats( new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 0, mUidStatsSize, new Bundle())); } } /** /** Loading services/tests/powerstatstests/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ android_test { "androidx.test.uiautomator_uiautomator", "androidx.test.uiautomator_uiautomator", "mockito-target-minus-junit4", "mockito-target-minus-junit4", "servicestests-utils", "servicestests-utils", "platform-test-annotations", "flag-junit", "flag-junit", ], ], Loading Loading
core/java/com/android/internal/os/PowerStats.java +206 −4 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,13 @@ package com.android.internal.os; package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.BatteryConsumer; import android.os.BatteryConsumer; import android.os.Bundle; import android.os.Parcel; import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.SparseArray; import android.util.SparseArray; import java.util.Arrays; import java.util.Arrays; Loading @@ -27,10 +32,138 @@ import java.util.Arrays; * details. * details. */ */ public final class PowerStats { public final class PowerStats { private static final String TAG = "PowerStats"; private static final BatteryStatsHistory.VarintParceler VARINT_PARCELER = new BatteryStatsHistory.VarintParceler(); private static final byte PARCEL_FORMAT_VERSION = 1; private static final int PARCEL_FORMAT_VERSION_MASK = 0x000000FF; private static final int PARCEL_FORMAT_VERSION_SHIFT = Integer.numberOfTrailingZeros(PARCEL_FORMAT_VERSION_MASK); private static final int STATS_ARRAY_LENGTH_MASK = 0x0000FF00; private static final int STATS_ARRAY_LENGTH_SHIFT = Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK); public static final int MAX_STATS_ARRAY_LENGTH = 2 ^ Integer.bitCount(STATS_ARRAY_LENGTH_MASK) - 1; private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000; private static final int UID_STATS_ARRAY_LENGTH_SHIFT = Integer.numberOfTrailingZeros(UID_STATS_ARRAY_LENGTH_MASK); public static final int MAX_UID_STATS_ARRAY_LENGTH = (2 ^ Integer.bitCount(UID_STATS_ARRAY_LENGTH_MASK)) - 1; /** * Descriptor of the stats collected for a given power component (e.g. CPU, WiFi etc). * This descriptor is used for storing PowerStats and can also be used by power models * to adjust the algorithm in accordance with the stats available on the device. */ public static class Descriptor { /** * {@link BatteryConsumer.PowerComponent} (e.g. CPU, WIFI etc) that this snapshot relates * to; or a custom power component ID (if the value * is >= {@link BatteryConsumer#FIRST_CUSTOM_POWER_COMPONENT_ID}). */ public final int powerComponentId; public final String name; public final int statsArrayLength; public final int uidStatsArrayLength; /** * Extra parameters specific to the power component, e.g. the availability of power * monitors. */ public final Bundle extras; public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId, int statsArrayLength, int uidStatsArrayLength, @NonNull Bundle extras) { this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId), statsArrayLength, uidStatsArrayLength, extras); } public Descriptor(int customPowerComponentId, String name, int statsArrayLength, int uidStatsArrayLength, Bundle extras) { if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH); } if (uidStatsArrayLength > MAX_UID_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "uidStatsArrayLength is too high. Max = " + MAX_UID_STATS_ARRAY_LENGTH); } this.powerComponentId = customPowerComponentId; this.name = name; this.statsArrayLength = statsArrayLength; this.uidStatsArrayLength = uidStatsArrayLength; this.extras = extras; } /** * Writes the Descriptor into the parcel. */ public void writeSummaryToParcel(Parcel parcel) { int firstWord = ((PARCEL_FORMAT_VERSION << PARCEL_FORMAT_VERSION_SHIFT) & PARCEL_FORMAT_VERSION_MASK) | ((statsArrayLength << STATS_ARRAY_LENGTH_SHIFT) & STATS_ARRAY_LENGTH_MASK) | ((uidStatsArrayLength << UID_STATS_ARRAY_LENGTH_SHIFT) & UID_STATS_ARRAY_LENGTH_MASK); parcel.writeInt(firstWord); parcel.writeInt(powerComponentId); parcel.writeString(name); extras.writeToParcel(parcel, 0); } /** * Reads a Descriptor from the parcel. If the parcel has an incompatible format, * returns null. */ @Nullable public static Descriptor readSummaryFromParcel(Parcel parcel) { int firstWord = parcel.readInt(); int version = (firstWord & PARCEL_FORMAT_VERSION_MASK) >>> PARCEL_FORMAT_VERSION_SHIFT; if (version != PARCEL_FORMAT_VERSION) { Log.w(TAG, "Cannot read PowerStats from Parcel - the parcel format version has " + "changed from " + version + " to " + PARCEL_FORMAT_VERSION); return null; } int statsArrayLength = (firstWord & STATS_ARRAY_LENGTH_MASK) >>> STATS_ARRAY_LENGTH_SHIFT; int uidStatsArrayLength = (firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT; int powerComponentId = parcel.readInt(); String name = parcel.readString(); Bundle extras = parcel.readBundle(PowerStats.class.getClassLoader()); return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength, extras); } } /** * A registry for all supported power component types (e.g. CPU, WiFi). */ public static class DescriptorRegistry { private final SparseArray<Descriptor> mDescriptors = new SparseArray<>(); /** /** * Power component (e.g. CPU, WIFI etc) that this snapshot relates to. * Adds the specified descriptor to the registry. If the registry already * contained a descriptor for the same power component, then the new one replaces * the old one. */ */ public @BatteryConsumer.PowerComponent int powerComponentId; public void register(Descriptor descriptor) { mDescriptors.put(descriptor.powerComponentId, descriptor); } /** * @param powerComponentId either a BatteryConsumer.PowerComponent or a custom power * component ID */ public Descriptor get(int powerComponentId) { return mDescriptors.get(powerComponentId); } } public final Descriptor descriptor; /** /** * Duration, in milliseconds, covered by this snapshot. * Duration, in milliseconds, covered by this snapshot. Loading @@ -47,12 +180,81 @@ public final class PowerStats { */ */ public final SparseArray<long[]> uidStats = new SparseArray<>(); public final SparseArray<long[]> uidStats = new SparseArray<>(); public PowerStats(Descriptor descriptor) { this.descriptor = descriptor; stats = new long[descriptor.statsArrayLength]; } /** * Writes the object into the parcel. */ public void writeToParcel(Parcel parcel) { int lengthPos = parcel.dataPosition(); parcel.writeInt(0); // Placeholder for length int startPos = parcel.dataPosition(); parcel.writeInt(descriptor.powerComponentId); parcel.writeLong(durationMs); VARINT_PARCELER.writeLongArray(parcel, stats); parcel.writeInt(uidStats.size()); for (int i = 0; i < uidStats.size(); i++) { parcel.writeInt(uidStats.keyAt(i)); VARINT_PARCELER.writeLongArray(parcel, uidStats.valueAt(i)); } int endPos = parcel.dataPosition(); parcel.setDataPosition(lengthPos); parcel.writeInt(endPos - startPos); parcel.setDataPosition(endPos); } /** * Reads a PowerStats object from the supplied Parcel. If the parcel has an incompatible * format, returns null. */ @Nullable public static PowerStats readFromParcel(Parcel parcel, DescriptorRegistry registry) { int length = parcel.readInt(); int startPos = parcel.dataPosition(); int endPos = startPos + length; try { int powerComponentId = parcel.readInt(); Descriptor descriptor = registry.get(powerComponentId); if (descriptor == null) { Log.e(TAG, "Unsupported PowerStats for power component ID: " + powerComponentId); return null; } PowerStats stats = new PowerStats(descriptor); stats.durationMs = parcel.readLong(); stats.stats = new long[descriptor.statsArrayLength]; VARINT_PARCELER.readLongArray(parcel, stats.stats); int uidCount = parcel.readInt(); for (int i = 0; i < uidCount; i++) { int uid = parcel.readInt(); long[] uidStats = new long[descriptor.uidStatsArrayLength]; VARINT_PARCELER.readLongArray(parcel, uidStats); stats.uidStats.put(uid, uidStats); } if (parcel.dataPosition() != endPos) { Log.e(TAG, "Corrupted PowerStats parcel. Expected length: " + length + ", actual length: " + (parcel.dataPosition() - startPos)); return null; } return stats; } finally { // Unconditionally skip to the end of the written data, even if the actual parcel // format is incompatible parcel.setDataPosition(endPos); } } /** /** * Prints the contents of the stats snapshot. * Prints the contents of the stats snapshot. */ */ public void dump(IndentingPrintWriter pw) { public void dump(IndentingPrintWriter pw) { pw.print("PowerStats: "); pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')'); pw.println(BatteryConsumer.powerComponentIdToString(powerComponentId)); pw.increaseIndent(); pw.increaseIndent(); pw.print("duration", durationMs).println(); pw.print("duration", durationMs).println(); for (int i = 0; i < uidStats.size(); i++) { for (int i = 0; i < uidStats.size(); i++) { Loading
core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +2 −1 Original line number Original line Diff line number Diff line Loading @@ -35,6 +35,7 @@ import org.junit.runners.Suite; LongArrayMultiStateCounterTest.class, LongArrayMultiStateCounterTest.class, LongMultiStateCounterTest.class, LongMultiStateCounterTest.class, PowerProfileTest.class, PowerProfileTest.class, PowerStatsTest.class, EnergyConsumerStatsTest.class EnergyConsumerStatsTest.class }) }) Loading
core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java 0 → 100644 +132 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; import android.os.BatteryConsumer; import android.os.Bundle; import android.os.Parcel; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest public class PowerStatsTest { private PowerStats.DescriptorRegistry mRegistry; private PowerStats.Descriptor mDescriptor; @Before public void setup() { mRegistry = new PowerStats.DescriptorRegistry(); Bundle extras = new Bundle(); extras.putBoolean("hasPowerMonitor", true); mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras); mRegistry.register(mDescriptor); } @Test public void parceling_compatibleParcel() { PowerStats stats = new PowerStats(mDescriptor); stats.durationMs = 1234; stats.stats[0] = 10; stats.stats[1] = 20; stats.stats[2] = 30; stats.uidStats.put(42, new long[] {40, 50}); stats.uidStats.put(99, new long[] {60, 70}); Parcel parcel = Parcel.obtain(); mDescriptor.writeSummaryToParcel(parcel); stats.writeToParcel(parcel); parcel.writeString("END"); Parcel newParcel = marshallAndUnmarshall(parcel); PowerStats.Descriptor newDescriptor = PowerStats.Descriptor.readSummaryFromParcel(newParcel); assertThat(newDescriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU); assertThat(newDescriptor.name).isEqualTo("cpu"); assertThat(newDescriptor.statsArrayLength).isEqualTo(3); assertThat(newDescriptor.uidStatsArrayLength).isEqualTo(2); assertThat(newDescriptor.extras.getBoolean("hasPowerMonitor")).isTrue(); mRegistry.register(newDescriptor); PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry); assertThat(newStats.durationMs).isEqualTo(1234); assertThat(newStats.stats).isEqualTo(new long[] {10, 20, 30}); assertThat(newStats.uidStats.size()).isEqualTo(2); assertThat(newStats.uidStats.get(42)).isEqualTo(new long[] {40, 50}); assertThat(newStats.uidStats.get(99)).isEqualTo(new long[] {60, 70}); String end = newParcel.readString(); assertThat(end).isEqualTo("END"); } @Test public void parceling_unrecognizedPowerComponent() { PowerStats stats = new PowerStats( new PowerStats.Descriptor(777, "luck", 3, 2, new Bundle())); stats.durationMs = 1234; Parcel parcel = Parcel.obtain(); stats.writeToParcel(parcel); parcel.writeString("END"); Parcel newParcel = marshallAndUnmarshall(parcel); PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry); assertThat(newStats).isNull(); String end = newParcel.readString(); assertThat(end).isEqualTo("END"); } @Test public void parceling_wrongArrayLength() { PowerStats stats = new PowerStats(mDescriptor); stats.stats = new long[5]; // Is expected to be 3 Parcel parcel = Parcel.obtain(); stats.writeToParcel(parcel); parcel.writeString("END"); Parcel newParcel = marshallAndUnmarshall(parcel); PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry); assertThat(newStats).isNull(); String end = newParcel.readString(); assertThat(end).isEqualTo("END"); } private static Parcel marshallAndUnmarshall(Parcel parcel) { byte[] bytes = parcel.marshall(); parcel.recycle(); Parcel newParcel = Parcel.obtain(); newParcel.unmarshall(bytes, 0, bytes.length); newParcel.setDataPosition(0); return newParcel; } }
services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +7 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.power.stats; package com.android.server.power.stats; import android.os.BatteryConsumer; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.util.SparseArray; import android.util.SparseArray; Loading @@ -42,7 +44,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private final int mUidStatsSize; private final int mUidStatsSize; // Reusable instance // Reusable instance private final PowerStats mCpuPowerStats = new PowerStats(); private final PowerStats mCpuPowerStats; private long mLastUpdateTimestampNanos; private long mLastUpdateTimestampNanos; public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, Loading @@ -69,6 +71,10 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { } } mUidStatsSize = powerProfile.getCpuPowerBracketCount(); mUidStatsSize = powerProfile.getCpuPowerBracketCount(); mTempUidStats = new long[mUidStatsSize]; mTempUidStats = new long[mUidStatsSize]; mCpuPowerStats = new PowerStats( new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 0, mUidStatsSize, new Bundle())); } } /** /** Loading
services/tests/powerstatstests/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ android_test { "androidx.test.uiautomator_uiautomator", "androidx.test.uiautomator_uiautomator", "mockito-target-minus-junit4", "mockito-target-minus-junit4", "servicestests-utils", "servicestests-utils", "platform-test-annotations", "flag-junit", "flag-junit", ], ], Loading