diff options
20 files changed, 955 insertions, 237 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index ca84b3563561..6a83cee10309 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -682,6 +682,7 @@ public abstract class BatteryConsumer { static class BatteryConsumerDataLayout { private static final Key[] KEY_ARRAY = new Key[0]; + public static final int POWER_MODEL_NOT_INCLUDED = -1; public final String[] customPowerComponentNames; public final int customPowerComponentCount; public final boolean powerModelsIncluded; @@ -713,7 +714,9 @@ public abstract class BatteryConsumer { // Declare the Key for the power component, ignoring other dimensions. perComponentKeys.add( new Key(componentId, PROCESS_STATE_ANY, - powerModelsIncluded ? columnIndex++ : -1, // power model + powerModelsIncluded + ? columnIndex++ + : POWER_MODEL_NOT_INCLUDED, // power model columnIndex++, // power columnIndex++ // usage duration )); @@ -736,7 +739,9 @@ public abstract class BatteryConsumer { perComponentKeys.add( new Key(componentId, processState, - powerModelsIncluded ? columnIndex++ : -1, // power model + powerModelsIncluded + ? columnIndex++ + : POWER_MODEL_NOT_INCLUDED, // power model columnIndex++, // power columnIndex++ // usage duration )); @@ -843,11 +848,27 @@ public abstract class BatteryConsumer { @SuppressWarnings("unchecked") @NonNull + public T addConsumedPower(@PowerComponent int componentId, double componentPower, + @PowerModel int powerModel) { + mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), + componentPower, powerModel); + return (T) this; + } + + @SuppressWarnings("unchecked") + @NonNull public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel); return (T) this; } + @SuppressWarnings("unchecked") + @NonNull + public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { + mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel); + return (T) this; + } + /** * Sets the amount of drain attributed to the specified custom drain type. * diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index cd52b5c0f7f3..ed3100251040 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -36,6 +36,7 @@ import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -586,7 +587,8 @@ public final class BatteryUsageStats implements Parcelable, Closeable { + "(" + BatteryConsumer.processStateToString(key.processState) + ")"; } printPowerComponent(pw, prefix, label, devicePowerMah, appsPowerMah, - deviceConsumer.getPowerModel(key), + mIncludesPowerModels ? deviceConsumer.getPowerModel(key) + : BatteryConsumer.POWER_MODEL_UNDEFINED, deviceConsumer.getUsageDurationMillis(key)); } } @@ -774,6 +776,15 @@ public final class BatteryUsageStats implements Parcelable, Closeable { super.finalize(); } + @Override + public String toString() { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + dump(pw, ""); + pw.flush(); + return sw.toString(); + } + /** * Builder for BatteryUsageStats. */ diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index 9e5f5399301c..9c11ad433b8f 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -15,6 +15,7 @@ */ package android.os; +import static android.os.BatteryConsumer.BatteryConsumerDataLayout.POWER_MODEL_NOT_INCLUDED; import static android.os.BatteryConsumer.POWER_COMPONENT_ANY; import static android.os.BatteryConsumer.PROCESS_STATE_ANY; import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED; @@ -118,7 +119,7 @@ class PowerComponents { @BatteryConsumer.PowerModel int getPowerModel(BatteryConsumer.Key key) { - if (key.mPowerModelColumnIndex == -1) { + if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { throw new IllegalStateException( "Power model IDs were not requested in the BatteryUsageStatsQuery"); } @@ -468,7 +469,7 @@ class PowerComponents { mMinConsumedPowerThreshold = minConsumedPowerThreshold; for (BatteryConsumer.Key[] keys : mData.layout.keys) { for (BatteryConsumer.Key key : keys) { - if (key.mPowerModelColumnIndex != -1) { + if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { mData.putInt(key.mPowerModelColumnIndex, POWER_MODEL_UNINITIALIZED); } } @@ -478,11 +479,19 @@ class PowerComponents { @NonNull public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower, int powerModel) { - if (Math.abs(componentPower) < mMinConsumedPowerThreshold) { - componentPower = 0; - } mData.putDouble(key.mPowerColumnIndex, componentPower); - if (key.mPowerModelColumnIndex != -1) { + if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { + mData.putInt(key.mPowerModelColumnIndex, powerModel); + } + return this; + } + + @NonNull + public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower, + int powerModel) { + mData.putDouble(key.mPowerColumnIndex, + mData.getDouble(key.mPowerColumnIndex) + componentPower); + if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { mData.putInt(key.mPowerModelColumnIndex, powerModel); } return this; @@ -496,9 +505,6 @@ class PowerComponents { */ @NonNull public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) { - if (Math.abs(componentPower) < mMinConsumedPowerThreshold) { - componentPower = 0; - } final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; if (index < 0 || index >= mData.layout.customPowerComponentCount) { throw new IllegalArgumentException( @@ -575,12 +581,12 @@ class PowerComponents { mData.getLong(key.mDurationColumnIndex) + otherData.getLong(otherKey.mDurationColumnIndex)); - if (key.mPowerModelColumnIndex == -1) { + if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { continue; } boolean undefined = false; - if (otherKey.mPowerModelColumnIndex == -1) { + if (otherKey.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { undefined = true; } else { final int powerModel = mData.getInt(key.mPowerModelColumnIndex); @@ -641,19 +647,26 @@ class PowerComponents { */ @NonNull public PowerComponents build() { - mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower()); - for (BatteryConsumer.Key[] keys : mData.layout.keys) { for (BatteryConsumer.Key key : keys) { - if (key.mPowerModelColumnIndex != -1) { + if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) { mData.putInt(key.mPowerModelColumnIndex, BatteryConsumer.POWER_MODEL_UNDEFINED); } } + + if (mMinConsumedPowerThreshold != 0) { + if (mData.getDouble(key.mPowerColumnIndex) < mMinConsumedPowerThreshold) { + mData.putDouble(key.mPowerColumnIndex, 0); + } + } } } + if (mData.getDouble(mData.layout.totalConsumedPowerColumnIndex) == 0) { + mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower()); + } return new PowerComponents(this); } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 699ac7b82f1d..4a2b4589a23d 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -125,6 +125,7 @@ import com.android.server.power.stats.BatteryStatsImpl; import com.android.server.power.stats.BatteryUsageStatsProvider; import com.android.server.power.stats.CpuAggregatedPowerStatsProcessor; import com.android.server.power.stats.PowerStatsAggregator; +import com.android.server.power.stats.PowerStatsExporter; import com.android.server.power.stats.PowerStatsScheduler; import com.android.server.power.stats.PowerStatsStore; import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; @@ -430,8 +431,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator, aggregatedPowerStatsSpanDuration, powerStatsAggregationPeriod, mPowerStatsStore, Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats); - mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerProfile, - mCpuScalingPolicies, mPowerStatsStore, Clock.SYSTEM_CLOCK); + PowerStatsExporter powerStatsExporter = + new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator); + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, + powerStatsExporter, mPowerProfile, mCpuScalingPolicies, + mPowerStatsStore, Clock.SYSTEM_CLOCK); mStats.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider, mPowerStatsStore); mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider); mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler); @@ -473,9 +477,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub } public void systemServicesReady() { + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats()); + mWorker.systemServicesReady(); mStats.systemServicesReady(mContext); mCpuWakeupStats.systemServicesReady(); - mWorker.systemServicesReady(); final INetworkManagementService nms = INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java index aadd03b25428..894226cf32c9 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java @@ -33,6 +33,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -265,4 +266,11 @@ class AggregatedPowerStats { ipw.decreaseIndent(); } } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + dump(new IndentingPrintWriter(sw)); + return sw.toString(); + } } diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index 52dbbcf81f84..303c2457165a 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -43,6 +43,8 @@ import java.util.List; public class BatteryUsageStatsProvider { private static final String TAG = "BatteryUsageStatsProv"; private final Context mContext; + private boolean mPowerStatsExporterEnabled; + private final PowerStatsExporter mPowerStatsExporter; private final PowerStatsStore mPowerStatsStore; private final PowerProfile mPowerProfile; private final CpuScalingPolicies mCpuScalingPolicies; @@ -50,9 +52,12 @@ public class BatteryUsageStatsProvider { private final Object mLock = new Object(); private List<PowerCalculator> mPowerCalculators; - public BatteryUsageStatsProvider(Context context, PowerProfile powerProfile, - CpuScalingPolicies cpuScalingPolicies, PowerStatsStore powerStatsStore, Clock clock) { + public BatteryUsageStatsProvider(Context context, + PowerStatsExporter powerStatsExporter, + PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies, + PowerStatsStore powerStatsStore, Clock clock) { mContext = context; + mPowerStatsExporter = powerStatsExporter; mPowerStatsStore = powerStatsStore; mPowerProfile = powerProfile; mCpuScalingPolicies = cpuScalingPolicies; @@ -66,7 +71,10 @@ public class BatteryUsageStatsProvider { // Power calculators are applied in the order of registration mPowerCalculators.add(new BatteryChargeCalculator()); - mPowerCalculators.add(new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile)); + if (mPowerStatsExporterEnabled) { + mPowerCalculators.add( + new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile)); + } mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile)); mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile)); if (!BatteryStats.checkWifiOnly(mContext)) { @@ -206,6 +214,11 @@ public class BatteryUsageStatsProvider { powerCalculator.calculate(batteryUsageStatsBuilder, stats, realtimeUs, uptimeUs, query); } + if (mPowerStatsExporterEnabled) { + mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder, + stats.getMonotonicStartTime(), stats.getMonotonicEndTime()); + } + if ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) { batteryUsageStatsBuilder.setBatteryHistory(stats.copyHistory()); @@ -370,4 +383,9 @@ public class BatteryUsageStatsProvider { } return builder.build(); } + + public void setPowerStatsExporterEnabled(boolean enabled) { + + mPowerStatsExporterEnabled = enabled; + } } diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java index f40eef2ed820..ed9414ff53a1 100644 --- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java @@ -64,7 +64,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces private PowerStats.Descriptor mLastUsedDescriptor; // Cached results of parsing of current PowerStats.Descriptor. Only refreshed when // mLastUsedDescriptor changes - private CpuPowerStatsCollector.StatsArrayLayout mStatsLayout; + private CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout; // Sequence of steps for power estimation and intermediate results. private PowerEstimationPlan mPlan; @@ -106,7 +106,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces } mLastUsedDescriptor = descriptor; - mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout(); + mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout(); mStatsLayout.fromExtras(descriptor.extras); mTmpDeviceStatsArray = new long[descriptor.statsArrayLength]; @@ -149,7 +149,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces if (mPlan == null) { mPlan = new PowerEstimationPlan(stats.getConfig()); - if (mStatsLayout.getCpuClusterEnergyConsumerCount() != 0) { + if (mStatsLayout.getEnergyConsumerCount() != 0) { initEnergyConsumerToPowerBracketMaps(); } } @@ -212,7 +212,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces * CL_2: [bracket3, bracket4] */ private void initEnergyConsumerToPowerBracketMaps() { - int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); + int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); int powerBracketCount = mStatsLayout.getCpuPowerBracketCount(); mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount]; @@ -294,7 +294,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces continue; } - intermediates.uptime += mStatsLayout.getUptime(mTmpDeviceStatsArray); + intermediates.uptime += mStatsLayout.getUsageDuration(mTmpDeviceStatsArray); for (int cluster = 0; cluster < mCpuClusterCount; cluster++) { intermediates.timeByCluster[cluster] += @@ -351,7 +351,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount(); int powerBracketCount = mStatsLayout.getCpuPowerBracketCount(); int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap(); - int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); + int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations; for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) { DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse); @@ -392,7 +392,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces private void adjustEstimatesUsingEnergyConsumers( Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) { - int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); + int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); if (energyConsumerCount == 0) { return; } @@ -509,8 +509,8 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces } sb.append(mStatsLayout.getTimeByCluster(stats, cluster)); } - sb.append("] uptime: ").append(mStatsLayout.getUptime(stats)); - int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); + sb.append("] uptime: ").append(mStatsLayout.getUsageDuration(stats)); + int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); if (energyConsumerCount > 0) { sb.append(" energy: ["); for (int i = 0; i < energyConsumerCount; i++) { diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java index 11828952227d..a1d42e5f1cf9 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java @@ -81,7 +81,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private PowerStats.Descriptor mPowerStatsDescriptor; // Reusable instance private PowerStats mCpuPowerStats; - private StatsArrayLayout mLayout; + private CpuStatsArrayLayout mLayout; private long mLastUpdateTimestampNanos; private long mLastUpdateUptimeMillis; private int mLastVoltageMv; @@ -91,55 +91,30 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { * Captures the positions and lengths of sections of the stats array, such as time-in-state, * power usage estimates etc. */ - public static class StatsArrayLayout { + public static class CpuStatsArrayLayout extends StatsArrayLayout { private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt"; private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc"; private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc"; private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc"; - private static final String EXTRA_DEVICE_POWER_POSITION = "dp"; - private static final String EXTRA_DEVICE_UPTIME_POSITION = "du"; - private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de"; - private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec"; private static final String EXTRA_UID_BRACKETS_POSITION = "ub"; private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us"; - private static final String EXTRA_UID_POWER_POSITION = "up"; - - private static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0; - - private int mDeviceStatsArrayLength; - private int mUidStatsArrayLength; private int mDeviceCpuTimeByScalingStepPosition; private int mDeviceCpuTimeByScalingStepCount; private int mDeviceCpuTimeByClusterPosition; private int mDeviceCpuTimeByClusterCount; - private int mDeviceCpuUptimePosition; - private int mDeviceEnergyConsumerPosition; - private int mDeviceEnergyConsumerCount; - private int mDevicePowerEstimatePosition; private int mUidPowerBracketsPosition; private int mUidPowerBracketCount; - private int[][] mEnergyConsumerToPowerBucketMaps; - private int mUidPowerEstimatePosition; private int[] mScalingStepToPowerBracketMap; - public int getDeviceStatsArrayLength() { - return mDeviceStatsArrayLength; - } - - public int getUidStatsArrayLength() { - return mUidStatsArrayLength; - } - /** * Declare that the stats array has a section capturing CPU time per scaling step */ public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) { - mDeviceCpuTimeByScalingStepPosition = mDeviceStatsArrayLength; + mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount); mDeviceCpuTimeByScalingStepCount = scalingStepCount; - mDeviceStatsArrayLength += scalingStepCount; } public int getCpuScalingStepCount() { @@ -166,9 +141,8 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { * Declare that the stats array has a section capturing CPU time in each cluster */ public void addDeviceSectionCpuTimeByCluster(int clusterCount) { + mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount); mDeviceCpuTimeByClusterCount = clusterCount; - mDeviceCpuTimeByClusterPosition = mDeviceStatsArrayLength; - mDeviceStatsArrayLength += clusterCount; } public int getCpuClusterCount() { @@ -192,86 +166,12 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { } /** - * Declare that the stats array has a section capturing CPU uptime - */ - public void addDeviceSectionUptime() { - mDeviceCpuUptimePosition = mDeviceStatsArrayLength++; - } - - /** - * Saves the CPU uptime duration in the corresponding <code>stats</code> element. - */ - public void setUptime(long[] stats, long value) { - stats[mDeviceCpuUptimePosition] = value; - } - - /** - * Extracts the CPU uptime duration from the corresponding <code>stats</code> element. - */ - public long getUptime(long[] stats) { - return stats[mDeviceCpuUptimePosition]; - } - - /** - * Declares that the stats array has a section capturing EnergyConsumer data from - * PowerStatsService. - */ - public void addDeviceSectionEnergyConsumers(int energyConsumerCount) { - mDeviceEnergyConsumerPosition = mDeviceStatsArrayLength; - mDeviceEnergyConsumerCount = energyConsumerCount; - mDeviceStatsArrayLength += energyConsumerCount; - } - - public int getCpuClusterEnergyConsumerCount() { - return mDeviceEnergyConsumerCount; - } - - /** - * Saves the accumulated energy for the specified rail the corresponding - * <code>stats</code> element. - */ - public void setConsumedEnergy(long[] stats, int index, long energy) { - stats[mDeviceEnergyConsumerPosition + index] = energy; - } - - /** - * Extracts the EnergyConsumer data from a device stats array for the specified - * EnergyConsumer. - */ - public long getConsumedEnergy(long[] stats, int index) { - return stats[mDeviceEnergyConsumerPosition + index]; - } - - /** - * Declare that the stats array has a section capturing a power estimate - */ - public void addDeviceSectionPowerEstimate() { - mDevicePowerEstimatePosition = mDeviceStatsArrayLength++; - } - - /** - * Converts the supplied mAh power estimate to a long and saves it in the corresponding - * element of <code>stats</code>. - */ - public void setDevicePowerEstimate(long[] stats, double power) { - stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); - } - - /** - * Extracts the power estimate from a device stats array and converts it to mAh. - */ - public double getDevicePowerEstimate(long[] stats) { - return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; - } - - /** * Declare that the UID stats array has a section capturing CPU time per power bracket. */ public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) { mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap; - mUidPowerBracketsPosition = mUidStatsArrayLength; updatePowerBracketCount(); - mUidStatsArrayLength += mUidPowerBracketCount; + mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount); } private void updatePowerBracketCount() { @@ -306,31 +206,10 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { } /** - * Declare that the UID stats array has a section capturing a power estimate - */ - public void addUidSectionPowerEstimate() { - mUidPowerEstimatePosition = mUidStatsArrayLength++; - } - - /** - * Converts the supplied mAh power estimate to a long and saves it in the corresponding - * element of <code>stats</code>. - */ - public void setUidPowerEstimate(long[] stats, double power) { - stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); - } - - /** - * Extracts the power estimate from a UID stats array and converts it to mAh. - */ - public double getUidPowerEstimate(long[] stats) { - return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; - } - - /** * Copies the elements of the stats array layout into <code>extras</code> */ public void toExtras(PersistableBundle extras) { + super.toExtras(extras); extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION, mDeviceCpuTimeByScalingStepPosition); extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT, @@ -339,22 +218,16 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { mDeviceCpuTimeByClusterPosition); extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT, mDeviceCpuTimeByClusterCount); - extras.putInt(EXTRA_DEVICE_UPTIME_POSITION, mDeviceCpuUptimePosition); - extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, - mDeviceEnergyConsumerPosition); - extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, - mDeviceEnergyConsumerCount); - extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition); extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition); - extras.putIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET, + putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET, mScalingStepToPowerBracketMap); - extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition); } /** * Retrieves elements of the stats array layout from <code>extras</code> */ public void fromExtras(PersistableBundle extras) { + super.fromExtras(extras); mDeviceCpuTimeByScalingStepPosition = extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION); mDeviceCpuTimeByScalingStepCount = @@ -363,18 +236,13 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION); mDeviceCpuTimeByClusterCount = extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT); - mDeviceCpuUptimePosition = extras.getInt(EXTRA_DEVICE_UPTIME_POSITION); - mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION); - mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT); - mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION); mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION); mScalingStepToPowerBracketMap = - extras.getIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET); + getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET); if (mScalingStepToPowerBracketMap == null) { mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount]; } updatePowerBracketCount(); - mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION); } } @@ -432,10 +300,10 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { mTempCpuTimeByScalingStep = new long[cpuScalingStepCount]; int[] scalingStepToPowerBracketMap = initPowerBrackets(); - mLayout = new StatsArrayLayout(); + mLayout = new CpuStatsArrayLayout(); mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount); mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length); - mLayout.addDeviceSectionUptime(); + mLayout.addDeviceSectionUsageDuration(); mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length); mLayout.addDeviceSectionPowerEstimate(); mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap); @@ -691,7 +559,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { if (uptimeDelta > mCpuPowerStats.durationMs) { uptimeDelta = mCpuPowerStats.durationMs; } - mLayout.setUptime(mCpuPowerStats.stats, uptimeDelta); + mLayout.setUsageDuration(mCpuPowerStats.stats, uptimeDelta); if (mCpuEnergyConsumerIds.length != 0) { collectEnergyConsumers(); 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 2c7843e626c9..721a15746834 100644 --- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java @@ -44,6 +44,7 @@ class PowerComponentAggregatedPowerStats { private static final String XML_TAG_DEVICE_STATS = "device-stats"; private static final String XML_TAG_UID_STATS = "uid-stats"; private static final String XML_ATTR_UID = "uid"; + private static final long UNKNOWN = -1; public final int powerComponentId; private final MultiStateStats.States[] mDeviceStateConfig; @@ -51,17 +52,16 @@ class PowerComponentAggregatedPowerStats { @NonNull private final AggregatedPowerStatsConfig.PowerComponent mConfig; private final int[] mDeviceStates; - private final long[] mDeviceStateTimestamps; private MultiStateStats.Factory mStatsFactory; private MultiStateStats.Factory mUidStatsFactory; private PowerStats.Descriptor mPowerStatsDescriptor; + private long mPowerStatsTimestamp; private MultiStateStats mDeviceStats; private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private static class UidStats { public int[] states; - public long[] stateTimestampMs; public MultiStateStats stats; } @@ -71,7 +71,7 @@ class PowerComponentAggregatedPowerStats { mDeviceStateConfig = config.getDeviceStateConfig(); mUidStateConfig = config.getUidStateConfig(); mDeviceStates = new int[mDeviceStateConfig.length]; - mDeviceStateTimestamps = new long[mDeviceStateConfig.length]; + mPowerStatsTimestamp = UNKNOWN; } @NonNull @@ -85,8 +85,11 @@ class PowerComponentAggregatedPowerStats { } void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time) { + if (mDeviceStats == null) { + createDeviceStats(); + } + mDeviceStates[stateId] = state; - mDeviceStateTimestamps[stateId] = time; if (mDeviceStateConfig[stateId].isTracked()) { if (mDeviceStats != null) { @@ -97,6 +100,11 @@ class PowerComponentAggregatedPowerStats { if (mUidStateConfig[stateId].isTracked()) { for (int i = mUidStats.size() - 1; i >= 0; i--) { PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i); + if (uidStats.stats == null) { + createUidStats(uidStats); + } + + uidStats.states[stateId] = state; if (uidStats.stats != null) { uidStats.stats.setState(stateId, state, time); } @@ -111,8 +119,11 @@ class PowerComponentAggregatedPowerStats { } UidStats uidStats = getUidStats(uid); + if (uidStats.stats == null) { + createUidStats(uidStats); + } + uidStats.states[stateId] = state; - uidStats.stateTimestampMs[stateId] = time; if (uidStats.stats != null) { uidStats.stats.setState(stateId, state, time); @@ -150,10 +161,11 @@ class PowerComponentAggregatedPowerStats { } uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs); } + + mPowerStatsTimestamp = timestampMs; } void reset() { - mPowerStatsDescriptor = null; mStatsFactory = null; mUidStatsFactory = null; mDeviceStats = null; @@ -168,7 +180,6 @@ class PowerComponentAggregatedPowerStats { if (uidStats == null) { uidStats = new UidStats(); uidStats.states = new int[mUidStateConfig.length]; - uidStats.stateTimestampMs = new long[mUidStateConfig.length]; mUidStats.put(uid, uidStats); } return uidStats; @@ -209,42 +220,38 @@ class PowerComponentAggregatedPowerStats { return false; } - private boolean createDeviceStats() { + private void createDeviceStats() { if (mStatsFactory == null) { if (mPowerStatsDescriptor == null) { - return false; + return; } mStatsFactory = new MultiStateStats.Factory( mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig); } mDeviceStats = mStatsFactory.create(); - for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) { - mDeviceStats.setState(stateId, mDeviceStates[stateId], - mDeviceStateTimestamps[stateId]); + if (mPowerStatsTimestamp != UNKNOWN) { + for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) { + mDeviceStats.setState(stateId, mDeviceStates[stateId], mPowerStatsTimestamp); + } } - return true; } - private boolean createUidStats(UidStats uidStats) { + private void createUidStats(UidStats uidStats) { if (mUidStatsFactory == null) { if (mPowerStatsDescriptor == null) { - return false; + return; } mUidStatsFactory = new MultiStateStats.Factory( mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig); } uidStats.stats = mUidStatsFactory.create(); - for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) { - uidStats.stats.setState(stateId, mDeviceStates[stateId], - mDeviceStateTimestamps[stateId]); - } - for (int stateId = mDeviceStateConfig.length; stateId < mUidStateConfig.length; stateId++) { - uidStats.stats.setState(stateId, uidStats.states[stateId], - uidStats.stateTimestampMs[stateId]); + for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) { + if (mPowerStatsTimestamp != UNKNOWN) { + uidStats.stats.setState(stateId, uidStats.states[stateId], mPowerStatsTimestamp); + } } - return true; } public void writeXml(TypedXmlSerializer serializer) throws IOException { diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java index 2f9d5674d78a..3f88a2d2dec0 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java @@ -29,13 +29,18 @@ import java.util.function.Consumer; * {@link AggregatedPowerStats} that adds up power stats from the samples found in battery history. */ public class PowerStatsAggregator { + private static final long UNINITIALIZED = -1; private final AggregatedPowerStats mStats; + private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig; private final BatteryStatsHistory mHistory; private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>(); + private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY; + private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig, BatteryStatsHistory history) { mStats = new AggregatedPowerStats(aggregatedPowerStatsConfig); + mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig; mHistory = history; for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig : aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) { @@ -44,6 +49,10 @@ public class PowerStatsAggregator { } } + AggregatedPowerStatsConfig getConfig() { + return mAggregatedPowerStatsConfig; + } + /** * Iterates of the battery history and aggregates power stats between the specified times. * The start and end are specified in the battery-stats monotonic time, which is the @@ -58,18 +67,20 @@ public class PowerStatsAggregator { */ public void aggregatePowerStats(long startTimeMs, long endTimeMs, Consumer<AggregatedPowerStats> consumer) { - int currentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY; - int currentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; - long baseTime = -1; + boolean clockUpdateAdded = false; + long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED; long lastTime = 0; try (BatteryStatsHistoryIterator iterator = mHistory.copy().iterate(startTimeMs, endTimeMs)) { while (iterator.hasNext()) { BatteryStats.HistoryItem item = iterator.next(); - if (baseTime < 0) { + if (!clockUpdateAdded) { mStats.addClockUpdate(item.time, item.currentTime); - baseTime = item.time; + if (baseTime == UNINITIALIZED) { + baseTime = item.time; + } + clockUpdateAdded = true; } else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME || item.cmd == BatteryStats.HistoryItem.CMD_RESET) { mStats.addClockUpdate(item.time, item.currentTime); @@ -81,20 +92,20 @@ public class PowerStatsAggregator { (item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0 ? AggregatedPowerStatsConfig.POWER_STATE_OTHER : AggregatedPowerStatsConfig.POWER_STATE_BATTERY; - if (batteryState != currentBatteryState) { + if (batteryState != mCurrentBatteryState) { mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_POWER, batteryState, item.time); - currentBatteryState = batteryState; + mCurrentBatteryState = batteryState; } int screenState = (item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0 ? AggregatedPowerStatsConfig.SCREEN_STATE_ON : AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; - if (screenState != currentScreenState) { + if (screenState != mCurrentScreenState) { mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN, screenState, item.time); - currentScreenState = screenState; + mCurrentScreenState = screenState; } if (item.processStateChange != null) { diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java index 1e41fdf12044..abfe9debc7de 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java @@ -19,8 +19,10 @@ package com.android.server.power.stats; import android.annotation.Nullable; import android.os.ConditionVariable; import android.os.Handler; +import android.os.PersistableBundle; import android.util.FastImmutableArraySet; import android.util.IndentingPrintWriter; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.Clock; @@ -39,6 +41,7 @@ import java.util.stream.Stream; * except where noted. */ public abstract class PowerStatsCollector { + private static final String TAG = "PowerStatsCollector"; private static final int MILLIVOLTS_PER_VOLT = 1000; private final Handler mHandler; protected final Clock mClock; @@ -47,6 +50,200 @@ public abstract class PowerStatsCollector { private boolean mEnabled; private long mLastScheduledUpdateMs = -1; + /** + * Captures the positions and lengths of sections of the stats array, such as usage duration, + * power usage estimates etc. + */ + public static class StatsArrayLayout { + private static final String EXTRA_DEVICE_POWER_POSITION = "dp"; + private static final String EXTRA_DEVICE_DURATION_POSITION = "dd"; + private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de"; + 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; + + private int mDeviceStatsArrayLength; + private int mUidStatsArrayLength; + + protected int mDeviceDurationPosition; + private int mDeviceEnergyConsumerPosition; + private int mDeviceEnergyConsumerCount; + private int mDevicePowerEstimatePosition; + private int mUidPowerEstimatePosition; + + public int getDeviceStatsArrayLength() { + return mDeviceStatsArrayLength; + } + + public int getUidStatsArrayLength() { + return mUidStatsArrayLength; + } + + protected int addDeviceSection(int length) { + int position = mDeviceStatsArrayLength; + mDeviceStatsArrayLength += length; + return position; + } + + protected int addUidSection(int length) { + int position = mUidStatsArrayLength; + mUidStatsArrayLength += length; + return position; + } + + /** + * Declare that the stats array has a section capturing usage duration + */ + public void addDeviceSectionUsageDuration() { + mDeviceDurationPosition = addDeviceSection(1); + } + + /** + * Saves the usage duration in the corresponding <code>stats</code> element. + */ + public void setUsageDuration(long[] stats, long value) { + stats[mDeviceDurationPosition] = value; + } + + /** + * Extracts the usage duration from the corresponding <code>stats</code> element. + */ + public long getUsageDuration(long[] stats) { + return stats[mDeviceDurationPosition]; + } + + /** + * Declares that the stats array has a section capturing EnergyConsumer data from + * PowerStatsService. + */ + public void addDeviceSectionEnergyConsumers(int energyConsumerCount) { + mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount); + mDeviceEnergyConsumerCount = energyConsumerCount; + } + + public int getEnergyConsumerCount() { + return mDeviceEnergyConsumerCount; + } + + /** + * Saves the accumulated energy for the specified rail the corresponding + * <code>stats</code> element. + */ + public void setConsumedEnergy(long[] stats, int index, long energy) { + stats[mDeviceEnergyConsumerPosition + index] = energy; + } + + /** + * Extracts the EnergyConsumer data from a device stats array for the specified + * EnergyConsumer. + */ + public long getConsumedEnergy(long[] stats, int index) { + return stats[mDeviceEnergyConsumerPosition + index]; + } + + /** + * Declare that the stats array has a section capturing a power estimate + */ + public void addDeviceSectionPowerEstimate() { + mDevicePowerEstimatePosition = addDeviceSection(1); + } + + /** + * Converts the supplied mAh power estimate to a long and saves it in the corresponding + * element of <code>stats</code>. + */ + public void setDevicePowerEstimate(long[] stats, double power) { + stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); + } + + /** + * Extracts the power estimate from a device stats array and converts it to mAh. + */ + public double getDevicePowerEstimate(long[] stats) { + return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; + } + + /** + * Declare that the UID stats array has a section capturing a power estimate + */ + public void addUidSectionPowerEstimate() { + mUidPowerEstimatePosition = addUidSection(1); + } + + /** + * Converts the supplied mAh power estimate to a long and saves it in the corresponding + * element of <code>stats</code>. + */ + public void setUidPowerEstimate(long[] stats, double power) { + stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); + } + + /** + * Extracts the power estimate from a UID stats array and converts it to mAh. + */ + public double getUidPowerEstimate(long[] stats) { + return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; + } + + /** + * Copies the elements of the stats array layout into <code>extras</code> + */ + public void toExtras(PersistableBundle extras) { + extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition); + extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, + mDeviceEnergyConsumerPosition); + extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, + mDeviceEnergyConsumerCount); + extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition); + extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition); + } + + /** + * Retrieves elements of the stats array layout from <code>extras</code> + */ + public void fromExtras(PersistableBundle extras) { + mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION); + mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION); + mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT); + mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION); + mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION); + } + + protected void putIntArray(PersistableBundle extras, String key, int[] array) { + if (array == null) { + return; + } + + StringBuilder sb = new StringBuilder(); + for (int value : array) { + if (!sb.isEmpty()) { + sb.append(','); + } + sb.append(value); + } + extras.putString(key, sb.toString()); + } + + protected int[] getIntArray(PersistableBundle extras, String key) { + String string = extras.getString(key); + if (string == null) { + return null; + } + String[] values = string.trim().split(","); + int[] result = new int[values.length]; + for (int i = 0; i < values.length; i++) { + try { + result[i] = Integer.parseInt(values[i]); + } catch (NumberFormatException e) { + Slog.wtf(TAG, "Invalid CSV format: " + string); + return null; + } + } + return result; + } + } + @GuardedBy("this") @SuppressWarnings("unchecked") private volatile FastImmutableArraySet<Consumer<PowerStats>> mConsumerList = diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java new file mode 100644 index 000000000000..70c24d58bb2a --- /dev/null +++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java @@ -0,0 +1,227 @@ +/* + * 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.server.power.stats; + +import android.os.AggregateBatteryConsumer; +import android.os.BatteryConsumer; +import android.os.BatteryUsageStats; +import android.os.UidBatteryConsumer; +import android.util.Slog; + +import com.android.internal.os.MultiStateStats; +import com.android.internal.os.PowerStats; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Given a time range, converts accumulated PowerStats to BatteryUsageStats. Combines + * stores spans of PowerStats with the yet-unprocessed tail of battery history. + */ +public class PowerStatsExporter { + private static final String TAG = "PowerStatsExporter"; + private final PowerStatsStore mPowerStatsStore; + private final PowerStatsAggregator mPowerStatsAggregator; + private final long mBatterySessionTimeSpanSlackMillis; + private static final long BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS = TimeUnit.MINUTES.toMillis(2); + + public PowerStatsExporter(PowerStatsStore powerStatsStore, + PowerStatsAggregator powerStatsAggregator) { + this(powerStatsStore, powerStatsAggregator, BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS); + } + + public PowerStatsExporter(PowerStatsStore powerStatsStore, + PowerStatsAggregator powerStatsAggregator, + long batterySessionTimeSpanSlackMillis) { + mPowerStatsStore = powerStatsStore; + mPowerStatsAggregator = powerStatsAggregator; + mBatterySessionTimeSpanSlackMillis = batterySessionTimeSpanSlackMillis; + } + + /** + * Populates the provided BatteryUsageStats.Builder with power estimates from the accumulated + * PowerStats, both stored in PowerStatsStore and not-yet processed. + */ + public void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder, + long monotonicStartTime, long monotonicEndTime) { + long maxEndTime = monotonicStartTime; + List<PowerStatsSpan.Metadata> spans = mPowerStatsStore.getTableOfContents(); + for (int i = spans.size() - 1; i >= 0; i--) { + PowerStatsSpan.Metadata metadata = spans.get(i); + if (!metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) { + continue; + } + + List<PowerStatsSpan.TimeFrame> timeFrames = metadata.getTimeFrames(); + long spanMinTime = Long.MAX_VALUE; + long spanMaxTime = Long.MIN_VALUE; + for (int j = 0; j < timeFrames.size(); j++) { + PowerStatsSpan.TimeFrame timeFrame = timeFrames.get(j); + long startMonotonicTime = timeFrame.startMonotonicTime; + long endMonotonicTime = startMonotonicTime + timeFrame.duration; + if (startMonotonicTime < spanMinTime) { + spanMinTime = startMonotonicTime; + } + if (endMonotonicTime > spanMaxTime) { + spanMaxTime = endMonotonicTime; + } + } + + if (!(spanMinTime >= monotonicStartTime && spanMaxTime < monotonicEndTime)) { + continue; + } + + if (spanMaxTime > maxEndTime) { + maxEndTime = spanMaxTime; + } + + PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(), + AggregatedPowerStatsSection.TYPE); + if (span == null) { + Slog.e(TAG, "Could not read PowerStatsStore section " + metadata); + continue; + } + List<PowerStatsSpan.Section> sections = span.getSections(); + for (int k = 0; k < sections.size(); k++) { + PowerStatsSpan.Section section = sections.get(k); + populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, + ((AggregatedPowerStatsSection) section).getAggregatedPowerStats()); + } + } + + if (maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) { + mPowerStatsAggregator.aggregatePowerStats(maxEndTime, monotonicEndTime, + stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats)); + } + } + + private void populateBatteryUsageStatsBuilder( + BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats) { + AggregatedPowerStatsConfig config = mPowerStatsAggregator.getConfig(); + List<AggregatedPowerStatsConfig.PowerComponent> powerComponents = + config.getPowerComponentsAggregatedStatsConfigs(); + for (int i = powerComponents.size() - 1; i >= 0; i--) { + populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats, + powerComponents.get(i)); + } + } + + private void populateBatteryUsageStatsBuilder( + BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats, + AggregatedPowerStatsConfig.PowerComponent powerComponent) { + int powerComponentId = powerComponent.getPowerComponentId(); + PowerComponentAggregatedPowerStats powerComponentStats = stats.getPowerComponentStats( + powerComponentId); + if (powerComponentStats == null) { + return; + } + + PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor(); + if (descriptor == null) { + return; + } + + PowerStatsCollector.StatsArrayLayout layout = new PowerStatsCollector.StatsArrayLayout(); + layout.fromExtras(descriptor.extras); + + long[] deviceStats = new long[descriptor.statsArrayLength]; + double[] totalPower = new double[1]; + MultiStateStats.States.forEachTrackedStateCombination(powerComponent.getDeviceStateConfig(), + states -> { + if (states[AggregatedPowerStatsConfig.STATE_POWER] + != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) { + return; + } + + if (!powerComponentStats.getDeviceStats(deviceStats, states)) { + return; + } + + totalPower[0] += layout.getDevicePowerEstimate(deviceStats); + }); + + AggregateBatteryConsumer.Builder deviceScope = + batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); + deviceScope.addConsumedPower(powerComponentId, + totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED); + + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + ArrayList<Integer> uids = new ArrayList<>(); + powerComponentStats.collectUids(uids); + + boolean breakDownByProcState = + batteryUsageStatsBuilder.isProcessStateDataNeeded() + && powerComponent + .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE] + .isTracked(); + + double[] powerByProcState = + new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1]; + double powerAllApps = 0; + for (int uid : uids) { + UidBatteryConsumer.Builder builder = + batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid); + + Arrays.fill(powerByProcState, 0); + + MultiStateStats.States.forEachTrackedStateCombination( + powerComponent.getUidStateConfig(), + states -> { + if (states[AggregatedPowerStatsConfig.STATE_POWER] + != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) { + return; + } + + if (!powerComponentStats.getUidStats(uidStats, uid, states)) { + return; + } + + double power = layout.getUidPowerEstimate(uidStats); + int procState = breakDownByProcState + ? states[AggregatedPowerStatsConfig.STATE_PROCESS_STATE] + : BatteryConsumer.PROCESS_STATE_UNSPECIFIED; + powerByProcState[procState] += power; + }); + + double powerAllProcStates = 0; + for (int procState = 0; procState < powerByProcState.length; procState++) { + double power = powerByProcState[procState]; + if (power == 0) { + continue; + } + powerAllProcStates += power; + if (breakDownByProcState + && procState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { + builder.addConsumedPower(builder.getKey(powerComponentId, procState), + power, BatteryConsumer.POWER_MODEL_UNDEFINED); + } + } + builder.addConsumedPower(powerComponentId, powerAllProcStates, + BatteryConsumer.POWER_MODEL_UNDEFINED); + powerAllApps += powerAllProcStates; + } + + AggregateBatteryConsumer.Builder allAppsScope = + batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); + allAppsScope.addConsumedPower(powerComponentId, powerAllApps, + BatteryConsumer.POWER_MODEL_UNDEFINED); + } +} diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java index 59085e83ac6b..97d872a1a539 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java @@ -75,6 +75,7 @@ public class PowerStatsScheduler { public void start(boolean enablePeriodicPowerStatsCollection) { mEnablePeriodicPowerStatsCollection = enablePeriodicPowerStatsCollection; if (mEnablePeriodicPowerStatsCollection) { + schedulePowerStatsAggregation(); scheduleNextPowerStatsAggregation(); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java index 2003d04e1dbc..ca7de7c3f325 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java @@ -90,6 +90,10 @@ public class AggregatedPowerStatsTest { private AggregatedPowerStats prepareAggregatePowerStats() { AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig); + + PowerStats ps = new PowerStats(mPowerComponentDescriptor); + stats.addPowerStats(ps, 0); + stats.addClockUpdate(1000, 456); stats.setDuration(789); @@ -100,7 +104,6 @@ public class AggregatedPowerStatsTest { stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE, BatteryConsumer.PROCESS_STATE_FOREGROUND, 2000); - PowerStats ps = new PowerStats(mPowerComponentDescriptor); ps.stats[0] = 100; ps.stats[1] = 987; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java index fd4e17c0da4b..8d51592667c8 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java @@ -106,6 +106,8 @@ public class BatteryStatsImplTest { private Handler mHandler; private PowerStatsStore mPowerStatsStore; private BatteryUsageStatsProvider mBatteryUsageStatsProvider; + @Mock + private PowerStatsExporter mPowerStatsExporter; @Before public void setUp() { @@ -130,8 +132,9 @@ public class BatteryStatsImplTest { File systemDir = context.getCacheDir(); mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, new AggregatedPowerStatsConfig()); - mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerProfile, - mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore, mMockClock); + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerStatsExporter, + mPowerProfile, mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore, + mMockClock); } @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java index 7aac73aadb45..7148b164efa9 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java @@ -69,7 +69,7 @@ public class BatteryUsageStatsProviderTest { BatteryStatsImpl batteryStats = prepareBatteryStats(); Context context = InstrumentationRegistry.getContext(); - BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock); final BatteryUsageStats batteryUsageStats = @@ -97,7 +97,7 @@ public class BatteryUsageStatsProviderTest { BatteryStatsImpl batteryStats = prepareBatteryStats(); Context context = InstrumentationRegistry.getContext(); - BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock); final BatteryUsageStats batteryUsageStats = @@ -203,7 +203,7 @@ public class BatteryUsageStatsProviderTest { } Context context = InstrumentationRegistry.getContext(); - BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock); final BatteryUsageStats batteryUsageStats = @@ -292,7 +292,7 @@ public class BatteryUsageStatsProviderTest { } Context context = InstrumentationRegistry.getContext(); - BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock); final BatteryUsageStats batteryUsageStats = @@ -379,7 +379,7 @@ public class BatteryUsageStatsProviderTest { mStatsRule.getHandler(), null); powerStatsStore.reset(); - BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock); @@ -502,7 +502,7 @@ public class BatteryUsageStatsProviderTest { when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE)) .thenReturn(span1); - BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java index 79084cc1b04d..8ca4ff6f86f5 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java @@ -192,7 +192,7 @@ public class CpuAggregatedPowerStatsProcessorTest { private static class MockPowerComponentAggregatedPowerStats extends PowerComponentAggregatedPowerStats { - private final CpuPowerStatsCollector.StatsArrayLayout mStatsLayout; + private final CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout; private final PowerStats.Descriptor mDescriptor; private HashMap<String, long[]> mDeviceStats = new HashMap<>(); private HashMap<String, long[]> mUidStats = new HashMap<>(); @@ -203,10 +203,10 @@ public class CpuAggregatedPowerStatsProcessorTest { MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config, boolean useEnergyConsumers) { super(config); - mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout(); + mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout(); mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3); mStatsLayout.addDeviceSectionCpuTimeByCluster(2); - mStatsLayout.addDeviceSectionUptime(); + mStatsLayout.addDeviceSectionUsageDuration(); if (useEnergyConsumers) { mStatsLayout.addDeviceSectionEnergyConsumers(2); } 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 bc211df5f143..c1d35227b090 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 @@ -156,14 +156,14 @@ public class CpuPowerStatsCollectorTest { assertThat(descriptor.name).isEqualTo("cpu"); assertThat(descriptor.statsArrayLength).isEqualTo(13); assertThat(descriptor.uidStatsArrayLength).isEqualTo(5); - CpuPowerStatsCollector.StatsArrayLayout layout = - new CpuPowerStatsCollector.StatsArrayLayout(); + CpuPowerStatsCollector.CpuStatsArrayLayout layout = + new CpuPowerStatsCollector.CpuStatsArrayLayout(); layout.fromExtras(descriptor.extras); long[] deviceStats = new long[descriptor.statsArrayLength]; layout.setTimeByScalingStep(deviceStats, 2, 42); layout.setConsumedEnergy(deviceStats, 1, 43); - layout.setUptime(deviceStats, 44); + layout.setUsageDuration(deviceStats, 44); layout.setDevicePowerEstimate(deviceStats, 45); long[] uidStats = new long[descriptor.uidStatsArrayLength]; @@ -173,10 +173,10 @@ public class CpuPowerStatsCollectorTest { assertThat(layout.getCpuScalingStepCount()).isEqualTo(7); assertThat(layout.getTimeByScalingStep(deviceStats, 2)).isEqualTo(42); - assertThat(layout.getCpuClusterEnergyConsumerCount()).isEqualTo(2); + assertThat(layout.getEnergyConsumerCount()).isEqualTo(2); assertThat(layout.getConsumedEnergy(deviceStats, 1)).isEqualTo(43); - assertThat(layout.getUptime(deviceStats)).isEqualTo(44); + assertThat(layout.getUsageDuration(deviceStats)).isEqualTo(44); assertThat(layout.getDevicePowerEstimate(deviceStats)).isEqualTo(45); @@ -195,8 +195,8 @@ public class CpuPowerStatsCollectorTest { mockEnergyConsumers(); CpuPowerStatsCollector collector = createCollector(8, 0); - CpuPowerStatsCollector.StatsArrayLayout layout = - new CpuPowerStatsCollector.StatsArrayLayout(); + CpuPowerStatsCollector.CpuStatsArrayLayout layout = + new CpuPowerStatsCollector.CpuStatsArrayLayout(); layout.fromExtras(collector.getPowerStatsDescriptor().extras); mockKernelCpuStats(new long[]{1111, 2222, 3333}, @@ -375,8 +375,8 @@ public class CpuPowerStatsCollectorTest { } private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) { - CpuPowerStatsCollector.StatsArrayLayout layout = - new CpuPowerStatsCollector.StatsArrayLayout(); + CpuPowerStatsCollector.CpuStatsArrayLayout layout = + new CpuPowerStatsCollector.CpuStatsArrayLayout(); layout.fromExtras(collector.getPowerStatsDescriptor().extras); return layout.getScalingStepToPowerBracketMap(); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java index b52fc8a7c727..67049871f396 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java @@ -76,9 +76,18 @@ public class PowerStatsAggregatorTest { @Test public void stateUpdates() { + PowerStats.Descriptor descriptor = + new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1, + new PersistableBundle()); + PowerStats powerStats = new PowerStats(descriptor); + mClock.currentTime = 1222156800000L; // An important date in world history mHistory.forceRecordAllHistory(); + powerStats.stats = new long[]{0}; + powerStats.uidStats.put(TEST_UID, new long[]{0}); + mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats); + mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true); mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG); @@ -87,10 +96,6 @@ public class PowerStatsAggregatorTest { advance(1000); - PowerStats.Descriptor descriptor = - new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1, - new PersistableBundle()); - PowerStats powerStats = new PowerStats(descriptor); powerStats.stats = new long[]{10000}; powerStats.uidStats.put(TEST_UID, new long[]{1234}); mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats); @@ -181,17 +186,21 @@ public class PowerStatsAggregatorTest { @Test public void incompatiblePowerStats() { + PowerStats.Descriptor descriptor = + new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1, + new PersistableBundle()); + PowerStats powerStats = new PowerStats(descriptor); + mHistory.forceRecordAllHistory(); + powerStats.stats = new long[]{0}; + powerStats.uidStats.put(TEST_UID, new long[]{0}); + mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats); mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true); mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID, BatteryConsumer.PROCESS_STATE_FOREGROUND); advance(1000); - PowerStats.Descriptor descriptor = - new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1, - new PersistableBundle()); - PowerStats powerStats = new PowerStats(descriptor); powerStats.stats = new long[]{10000}; powerStats.uidStats.put(TEST_UID, new long[]{1234}); mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java new file mode 100644 index 000000000000..3c482625b038 --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java @@ -0,0 +1,316 @@ +/* + * 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.server.power.stats; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; + +import android.os.AggregateBatteryConsumer; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.PersistableBundle; +import android.os.UidBatteryConsumer; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.os.BatteryStatsHistory; +import com.android.internal.os.MonotonicClock; +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class PowerStatsExporterTest { + + private static final int APP_UID1 = 42; + private static final int APP_UID2 = 84; + private static final double TOLERANCE = 0.01; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720) + .setCpuScalingPolicy(0, new int[]{0}, new int[]{100}) + .setAveragePowerForCpuScalingPolicy(0, 360) + .setAveragePowerForCpuScalingStep(0, 0, 300) + .setCpuPowerBracketCount(1) + .setCpuPowerBracket(0, 0, 0); + + private MockClock mClock = new MockClock(); + private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock); + private PowerStatsStore mPowerStatsStore; + private PowerStatsAggregator mPowerStatsAggregator; + private BatteryStatsHistory mHistory; + private CpuPowerStatsCollector.CpuStatsArrayLayout mCpuStatsArrayLayout; + private PowerStats.Descriptor mPowerStatsDescriptor; + + @Before + public void setup() { + File storeDirectory = new File(getContext().getCacheDir(), getClass().getSimpleName()); + clearDirectory(storeDirectory); + + AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU) + .trackDeviceStates(AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates(AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new CpuAggregatedPowerStatsProcessor(mStatsRule.getPowerProfile(), + mStatsRule.getCpuScalingPolicies())); + + mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config); + mHistory = new BatteryStatsHistory(Parcel.obtain(), storeDirectory, 0, 10000, + mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock, + mMonotonicClock, null); + mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory); + + mCpuStatsArrayLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout(); + mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1); + mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1); + mCpuStatsArrayLayout.addDeviceSectionPowerEstimate(); + mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0}); + mCpuStatsArrayLayout.addUidSectionPowerEstimate(); + PersistableBundle extras = new PersistableBundle(); + mCpuStatsArrayLayout.toExtras(extras); + + mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, + mCpuStatsArrayLayout.getDeviceStatsArrayLength(), + mCpuStatsArrayLayout.getUidStatsArrayLength(), extras); + } + + @Test + public void breakdownByProcState_fullRange() throws Exception { + BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( + new String[0], /* includePowerModels */ false, + /* includeProcessStateData */ true, /* powerThreshold */ 0); + exportAggregatedPowerStats(builder, 1000, 10000); + + BatteryUsageStats actual = builder.build(); + String message = "Actual BatteryUsageStats: " + actual; + + assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); + assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); + + assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_ANY, 13.5); + assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_FOREGROUND, 7.47); + assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_BACKGROUND, 6.03); + + assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_ANY, 12.03); + assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 12.03); + + actual.close(); + } + + @Test + public void breakdownByProcState_subRange() throws Exception { + BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( + new String[0], /* includePowerModels */ false, + /* includeProcessStateData */ true, /* powerThreshold */ 0); + exportAggregatedPowerStats(builder, 3700, 6700); + + BatteryUsageStats actual = builder.build(); + String message = "Actual BatteryUsageStats: " + actual; + + assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4); + assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4); + + assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_ANY, 4.06); + assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_FOREGROUND, 1.35); + assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_BACKGROUND, 2.70); + + assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_ANY, 11.33); + assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 11.33); + + actual.close(); + } + + @Test + public void combinedProcessStates() throws Exception { + BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( + new String[0], /* includePowerModels */ false, + /* includeProcessStateData */ false, /* powerThreshold */ 0); + exportAggregatedPowerStats(builder, 1000, 10000); + + BatteryUsageStats actual = builder.build(); + String message = "Actual BatteryUsageStats: " + actual; + + assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); + assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); + + assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_ANY, 13.5); + assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_ANY, 12.03); + UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream() + .filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null); + // There shouldn't be any per-procstate data + assertThrows( + IllegalArgumentException.class, + () -> uidScope.getConsumedPower(new BatteryConsumer.Dimensions( + BatteryConsumer.POWER_COMPONENT_CPU, + BatteryConsumer.PROCESS_STATE_FOREGROUND))); + + + actual.close(); + } + + private void recordBatteryHistory() { + PowerStats powerStats = new PowerStats(mPowerStatsDescriptor); + long[] uidStats1 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()]; + powerStats.uidStats.put(APP_UID1, uidStats1); + long[] uidStats2 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()]; + powerStats.uidStats.put(APP_UID2, uidStats2); + + mHistory.forceRecordAllHistory(); + + mHistory.startRecordingHistory(1000, 1000, false); + mHistory.recordPowerStats(1000, 1000, powerStats); + mHistory.recordBatteryState(1000, 1000, 70, /* plugged */ false); + mHistory.recordStateStartEvent(1000, 1000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG); + mHistory.recordProcessStateChange(1000, 1000, APP_UID1, + BatteryConsumer.PROCESS_STATE_FOREGROUND); + mHistory.recordProcessStateChange(1000, 1000, APP_UID2, + BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE); + + mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 11111); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 10000); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 1111); + mHistory.recordPowerStats(1000, 1000, powerStats); + + mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 12345); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 9876); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 2469); + mHistory.recordPowerStats(3000, 3000, powerStats); + + mPowerStatsAggregator.aggregatePowerStats(0, 3500, stats -> { + mPowerStatsStore.storeAggregatedPowerStats(stats); + }); + + mHistory.recordProcessStateChange(4000, 4000, APP_UID1, + BatteryConsumer.PROCESS_STATE_BACKGROUND); + + mHistory.recordStateStopEvent(4000, 4000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG); + + mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 54321); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 14321); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 40000); + mHistory.recordPowerStats(6000, 6000, powerStats); + + mPowerStatsAggregator.aggregatePowerStats(3500, 6500, stats -> { + mPowerStatsStore.storeAggregatedPowerStats(stats); + }); + + mHistory.recordStateStartEvent(7000, 7000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG); + mHistory.recordProcessStateChange(7000, 7000, APP_UID1, + BatteryConsumer.PROCESS_STATE_FOREGROUND); + mHistory.recordProcessStateChange(7000, 7000, APP_UID2, + BatteryConsumer.PROCESS_STATE_UNSPECIFIED); + + mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 23456); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 23456); + mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 0); + mHistory.recordPowerStats(8000, 8000, powerStats); + + assertThat(mPowerStatsStore.getTableOfContents()).hasSize(2); + } + + private void exportAggregatedPowerStats(BatteryUsageStats.Builder builder, + int monotonicStartTime, int monotonicEndTime) { + recordBatteryHistory(); + PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore, + mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0); + exporter.exportAggregatedPowerStats(builder, monotonicStartTime, monotonicEndTime); + } + + private void assertDevicePowerEstimate(String message, BatteryUsageStats bus, int componentId, + double expected) { + AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); + assertWithMessage(message).that(consumer.getConsumedPower(componentId)) + .isWithin(TOLERANCE).of(expected); + } + + private void assertAllAppsPowerEstimate(String message, BatteryUsageStats bus, int componentId, + double expected) { + AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); + assertWithMessage(message).that(consumer.getConsumedPower(componentId)) + .isWithin(TOLERANCE).of(expected); + } + + private void assertUidPowerEstimate(String message, BatteryUsageStats bus, int uid, + int componentId, int processState, double expected) { + List<UidBatteryConsumer> uidScopes = bus.getUidBatteryConsumers(); + final UidBatteryConsumer uidScope = uidScopes.stream() + .filter(us -> us.getUid() == uid).findFirst().orElse(null); + assertWithMessage(message).that(uidScope).isNotNull(); + assertWithMessage(message).that(uidScope.getConsumedPower( + new BatteryConsumer.Dimensions(componentId, processState))) + .isWithin(TOLERANCE).of(expected); + } + + private void clearDirectory(File dir) { + if (dir.exists()) { + for (File child : dir.listFiles()) { + if (child.isDirectory()) { + clearDirectory(child); + } + child.delete(); + } + } + } + + private static class TestHandler extends Handler { + TestHandler() { + super(Looper.getMainLooper()); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + msg.getCallback().run(); + return true; + } + } +} |