diff options
5 files changed, 529 insertions, 243 deletions
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index f2d8daf74a5a..1f7a7aa42669 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -21,8 +21,6 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.os.BatteryStatsManager.NUM_WIFI_STATES; import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES; -import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -107,7 +105,7 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeRea import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; import com.android.internal.power.MeasuredEnergyStats; -import com.android.internal.power.MeasuredEnergyStats.EnergyBucket; +import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; @@ -173,7 +171,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 192 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 193 + (USE_OLD_HISTORY ? 1000 : 0); // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -7161,8 +7159,8 @@ public class BatteryStatsImpl extends BatteryStats { if (mGlobalMeasuredEnergyStats == null) { return ENERGY_DATA_UNAVAILABLE; } - return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy( - MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON); + return mGlobalMeasuredEnergyStats + .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON); } @Override @@ -7170,8 +7168,8 @@ public class BatteryStatsImpl extends BatteryStats { if (mGlobalMeasuredEnergyStats == null) { return ENERGY_DATA_UNAVAILABLE; } - return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy( - MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE); + return mGlobalMeasuredEnergyStats + .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE); } @Override public long getStartClockTime() { @@ -7935,27 +7933,27 @@ public class BatteryStatsImpl extends BatteryStats { return mUidMeasuredEnergyStats; } - /** Adds the given energy to the given energy bucket for this uid. */ - private void addEnergyToEnergyBucketLocked(long energyDeltaUJ, - @MeasuredEnergyStats.EnergyBucket int energyBucket, boolean accumulate) { + /** Adds the given energy to the given standard energy bucket for this uid. */ + private void addEnergyToStandardBucketLocked(long energyDeltaUJ, + @StandardEnergyBucket int energyBucket, boolean accumulate) { getOrCreateMeasuredEnergyStatsLocked() - .updateBucket(energyBucket, energyDeltaUJ, accumulate); + .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate); } /** - * Returns the energy used by this uid for an energy bucket of interest. - * @param bucket energy bucket of interest + * Returns the energy used by this uid for a standard energy bucket of interest. + * @param bucket standard energy bucket of interest * @return energy (in microjoules) used by this uid for this energy bucket */ - public long getMeasuredEnergyMicroJoules(@MeasuredEnergyStats.EnergyBucket int bucket) { + public long getMeasuredEnergyMicroJoules(@StandardEnergyBucket int bucket) { if (mBsi.mGlobalMeasuredEnergyStats == null - || !mBsi.mGlobalMeasuredEnergyStats.isEnergyBucketSupported(bucket)) { + || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) { return ENERGY_DATA_UNAVAILABLE; } if (mUidMeasuredEnergyStats == null) { return 0L; // It is supported, but was never filled, so it must be 0 } - return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(bucket); + return mUidMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket); } /** @@ -12396,7 +12394,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } - final @EnergyBucket int energyBucket = + final @StandardEnergyBucket int energyBucket = MeasuredEnergyStats.getDisplayEnergyBucket(mScreenStateAtLastEnergyMeasurement); mScreenStateAtLastEnergyMeasurement = screenState; @@ -12415,7 +12413,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } - mGlobalMeasuredEnergyStats.updateBucket(energyBucket, energyUJ, true); + mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true); // Now we blame individual apps, but only if the display was ON. if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) { @@ -12453,7 +12451,7 @@ public class BatteryStatsImpl extends BatteryStats { final long appDisplayEnergyMJ = (totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2)) / totalFgTimeMs; - uid.addEnergyToEnergyBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true); + uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true); // To mitigate round-off errors, remove this app from numerator & denominator totals totalDisplayEnergyMJ -= appDisplayEnergyMJ; @@ -14138,33 +14136,34 @@ public class BatteryStatsImpl extends BatteryStats { /** * Initialize the measured energy stats data structures. * - * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported + * @param supportedStandardBuckets boolean array indicating which {@link StandardEnergyBucket}s + * are currently supported. + * If null, none are supported (regardless of numCustomBuckets). + * @param numCustomBuckets number of custom (OTHER) EnergyConsumers on this device */ @GuardedBy("this") - public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) { + public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets, + int numCustomBuckets) { boolean supportedBucketMismatch = false; mScreenStateAtLastEnergyMeasurement = mScreenState; - if (supportedEnergyBuckets == null) { + if (supportedStandardBuckets == null) { if (mGlobalMeasuredEnergyStats != null) { // Measured energy buckets no longer supported, wipe out the existing data. supportedBucketMismatch = true; } } else if (mGlobalMeasuredEnergyStats == null) { - mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets); + mGlobalMeasuredEnergyStats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); return; } else { - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i) - != supportedEnergyBuckets[i]) { - mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets); - supportedBucketMismatch = true; - break; - } - } + supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo( + supportedStandardBuckets, numCustomBuckets); } if (supportedBucketMismatch) { + mGlobalMeasuredEnergyStats = supportedStandardBuckets == null ? + null : new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); // Supported energy buckets changed since last boot. // Existing data is no longer reliable. resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime()); diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index b744a5d57e98..e310f8d9b36e 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -20,8 +20,10 @@ package com.android.internal.power; import static android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; +import android.util.DebugUtils; import android.util.Slog; import android.view.Display; @@ -33,7 +35,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Tracks the measured energy usage of various subsystems according to their {@link EnergyBucket}. + * Tracks the measured energy usage of various subsystems according to their + * {@link StandardEnergyBucket} or custom energy bucket (which is tied to + * {@link android.hardware.power.stats.EnergyConsumer.ordinal}). * * This class doesn't use a TimeBase, and instead requires manually decisions about when to * accumulate since it is trivial. However, in the future, a TimeBase could be used instead. @@ -42,15 +46,13 @@ import java.lang.annotation.RetentionPolicy; public class MeasuredEnergyStats { private static final String TAG = "MeasuredEnergyStats"; - // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if energy - // bucket integers are modified. + // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} MUST be updated if standard + // energy bucket integers are modified/added/removed. public static final int ENERGY_BUCKET_UNKNOWN = -1; public static final int ENERGY_BUCKET_SCREEN_ON = 0; public static final int ENERGY_BUCKET_SCREEN_DOZE = 1; public static final int ENERGY_BUCKET_SCREEN_OTHER = 2; - public static final int NUMBER_ENERGY_BUCKETS = 3; - private static final String[] ENERGY_BUCKET_NAMES = - {"screen-on", "screen-doze", "screen-other"}; + public static final int NUMBER_STANDARD_ENERGY_BUCKETS = 3; // Buckets above this are custom. @IntDef(prefix = {"ENERGY_BUCKET_"}, value = { ENERGY_BUCKET_UNKNOWN, @@ -59,28 +61,37 @@ public class MeasuredEnergyStats { ENERGY_BUCKET_SCREEN_OTHER, }) @Retention(RetentionPolicy.SOURCE) - public @interface EnergyBucket { + public @interface StandardEnergyBucket { } /** - * Total energy (in microjoules) that an {@link EnergyBucket} has accumulated since the last - * reset. Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs + * Total energy (in microjoules) that an energy bucket (including both + * {@link StandardEnergyBucket} and custom buckets) has accumulated since the last reset. + * Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs * while the necessary conditions are satisfied (e.g. on battery). * + * Energy for both {@link StandardEnergyBucket}s and custom energy buckets are stored in this + * array, and may internally both referred to as 'buckets'. This is an implementation detail; + * externally, we differentiate between these two data sources. + * * Warning: Long array is used for access speed. If the number of supported subsystems * becomes large, consider using an alternate data structure such as a SparseLongArray. */ - private final long[] mAccumulatedEnergiesMicroJoules = new long[NUMBER_ENERGY_BUCKETS]; + private final long[] mAccumulatedEnergiesMicroJoules; /** * Creates a MeasuredEnergyStats set to support the provided energy buckets. - * supportedEnergyBuckets should generally be of size {@link #NUMBER_ENERGY_BUCKETS}. + * supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_ENERGY_BUCKETS}. + * numCustomBuckets >= 0 is the number of (non-standard) custom energy buckets on the device. */ - public MeasuredEnergyStats(boolean[] supportedEnergyBuckets) { + public MeasuredEnergyStats(boolean[] supportedStandardBuckets, int numCustomBuckets) { + final int numTotalBuckets = NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets; + mAccumulatedEnergiesMicroJoules = new long[numTotalBuckets]; // Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE. - for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { - if (!supportedEnergyBuckets[bucket]) { - mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE; + // All custom buckets are, by definition, supported, so their values stay at 0. + for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) { + if (!supportedStandardBuckets[stdBucket]) { + mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE; } } } @@ -90,10 +101,13 @@ public class MeasuredEnergyStats { * supported. This certainly does NOT produce an exact clone of the template. */ private MeasuredEnergyStats(MeasuredEnergyStats template) { + final int numIndices = template.getNumberOfIndices(); + mAccumulatedEnergiesMicroJoules = new long[numIndices]; // Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE. - for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { - if (!template.isEnergyBucketSupported(bucket)) { - mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE; + // All custom buckets are, by definition, supported, so their values stay at 0. + for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) { + if (!template.isIndexSupported(stdBucket)) { + mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE; } } } @@ -108,18 +122,22 @@ public class MeasuredEnergyStats { /** * Constructor for creating a temp MeasuredEnergyStats. - * See {@link #readSummaryFromParcel(MeasuredEnergyStats, Parcel)}. + * See {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}. */ - private MeasuredEnergyStats() { + private MeasuredEnergyStats(int numIndices) { + mAccumulatedEnergiesMicroJoules = new long[numIndices]; } /** Construct from parcel. */ public MeasuredEnergyStats(Parcel in) { + final int size = in.readInt(); + mAccumulatedEnergiesMicroJoules = new long[size]; in.readLongArray(mAccumulatedEnergiesMicroJoules); } /** Write to parcel */ public void writeToParcel(Parcel out) { + out.writeInt(mAccumulatedEnergiesMicroJoules.length); out.writeLongArray(mAccumulatedEnergiesMicroJoules); } @@ -129,16 +147,18 @@ public class MeasuredEnergyStats { * summary parcel was written. Availability has already been correctly set in the constructor. * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary * parceling changes. + * + * Corresponding write performed by {@link #writeSummaryToParcel(Parcel, boolean)}. */ private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) { - final int size = in.readInt(); - for (int i = 0; i < size; i++) { - final int bucket = in.readInt(); + final int numWrittenEntries = in.readInt(); + for (int entry = 0; entry < numWrittenEntries; entry++) { + final int index = in.readInt(); final long energyUJ = in.readLong(); if (overwriteAvailability) { - mAccumulatedEnergiesMicroJoules[bucket] = energyUJ; + mAccumulatedEnergiesMicroJoules[index] = energyUJ; } else { - setValueIfSupported(bucket, energyUJ); + setValueIfSupported(index, energyUJ); } } } @@ -146,52 +166,90 @@ public class MeasuredEnergyStats { /** * Write to summary parcel. * Note: Measured subsystem availability may be different when the summary parcel is read. + * + * Corresponding read performed by {@link #readSummaryFromParcel(Parcel, boolean)}. */ private void writeSummaryToParcel(Parcel out, boolean skipZero) { - final int sizePos = out.dataPosition(); + final int posOfNumWrittenEntries = out.dataPosition(); out.writeInt(0); - int size = 0; - // Write only the supported buckets with non-zero energy. - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - final long energy = mAccumulatedEnergiesMicroJoules[i]; + int numWrittenEntries = 0; + // Write only the supported buckets (with non-zero energy, if applicable). + for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) { + final long energy = mAccumulatedEnergiesMicroJoules[index]; if (energy < 0) continue; if (energy == 0 && skipZero) continue; - out.writeInt(i); - out.writeLong(mAccumulatedEnergiesMicroJoules[i]); - size++; + out.writeInt(index); + out.writeLong(mAccumulatedEnergiesMicroJoules[index]); + numWrittenEntries++; } final int currPos = out.dataPosition(); - out.setDataPosition(sizePos); - out.writeInt(size); + out.setDataPosition(posOfNumWrittenEntries); + out.writeInt(numWrittenEntries); out.setDataPosition(currPos); } - /** Updates the given bucket with the given energy iff accumulate is true. */ - public void updateBucket(@EnergyBucket int bucket, long energyDeltaUJ, boolean accumulate) { + /** Get number of possible buckets, including both standard and custom ones. */ + private int getNumberOfIndices() { + return mAccumulatedEnergiesMicroJoules.length; + } + + // TODO: Get rid of the 'accumulate' boolean. It's always true. + /** Updates the given standard energy bucket with the given energy if accumulate is true. */ + public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ, + boolean accumulate) { + checkValidStandardBucket(bucket); + updateEntry(bucket, energyDeltaUJ, accumulate); + } + + /** Updates the given custom energy bucket with the given energy if accumulate is true. */ + public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) { + if (!isValidCustomBucket(customBucket)) { + Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket); + return; + } + final int index = customBucketToIndex(customBucket); + updateEntry(index, energyDeltaUJ, accumulate); + } + + /** Updates the given index with the given energy if accumulate is true. */ + private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) { if (accumulate) { - if (mAccumulatedEnergiesMicroJoules[bucket] >= 0L) { - mAccumulatedEnergiesMicroJoules[bucket] += energyDeltaUJ; + if (mAccumulatedEnergiesMicroJoules[index] >= 0L) { + mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ; } else { Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket " - + ENERGY_BUCKET_NAMES[bucket] + " whose value was " - + mAccumulatedEnergiesMicroJoules[bucket]); + + getBucketName(index) + " whose value was " + + mAccumulatedEnergiesMicroJoules[index]); } } } /** - * Return accumulated energy (in microjoules) for the given energy bucket since last reset. - * Returns {@link BatteryStats#ENERGY_DATA_UNAVAILABLE} if this energy data is unavailable. + * Return accumulated energy (in microjoules) for a standard energy bucket since last reset. + * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable. + * @throws IllegalArgumentException if no such {@link StandardEnergyBucket}. */ - public long getAccumulatedBucketEnergy(@EnergyBucket int bucket) { + public long getAccumulatedStandardBucketEnergy(@StandardEnergyBucket int bucket) { + checkValidStandardBucket(bucket); return mAccumulatedEnergiesMicroJoules[bucket]; } /** - * Map {@link MeasuredEnergySubsystem} and device state to a Display {@link EnergyBucket}. + * Return accumulated energy (in microjoules) for the a custom energy bucket since last reset. + * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable. */ - public static @EnergyBucket int getDisplayEnergyBucket(int screenState) { + public long getAccumulatedCustomBucketEnergy(int customBucket) { + if (!isValidCustomBucket(customBucket)) { + return ENERGY_DATA_UNAVAILABLE; + } + return mAccumulatedEnergiesMicroJoules[customBucketToIndex(customBucket)]; + } + + /** + * Map {@link MeasuredEnergySubsystem} and device state to Display {@link StandardEnergyBucket}. + */ + public static @StandardEnergyBucket int getDisplayEnergyBucket(int screenState) { if (Display.isOnState(screenState)) { return ENERGY_BUCKET_SCREEN_ON; } @@ -204,15 +262,20 @@ public class MeasuredEnergyStats { /** * Create a MeasuredEnergyStats object from a summary parcel. * + * Corresponding write performed by + * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}. + * * @return a new MeasuredEnergyStats object as described. * Returns null if the parcel indicates there is no data to populate. */ public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) { + final int arraySize = in.readInt(); // Check if any MeasuredEnergyStats exists on the parcel - if (in.readInt() == 0) return null; + if (arraySize == 0) return null; - final MeasuredEnergyStats stats = - new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]); + final int numCustomBuckets = arraySize - NUMBER_STANDARD_ENERGY_BUCKETS; + final MeasuredEnergyStats stats = new MeasuredEnergyStats( + new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], numCustomBuckets); stats.readSummaryFromParcel(in, true); return stats; } @@ -221,6 +284,12 @@ public class MeasuredEnergyStats { * Create a MeasuredEnergyStats using the template to determine which buckets are supported, * and populate this new object from the given parcel. * + * The parcel must be consistent with the template in terms of the number of + * possible (not necessarily supported) standard and custom buckets. + * + * Corresponding write performed by + * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}. + * * @return a new MeasuredEnergyStats object as described. * Returns null if the stats contain no non-0 information (such as if template is null * or if the parcel indicates there is no data to populate). @@ -229,12 +298,22 @@ public class MeasuredEnergyStats { */ public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in, @Nullable MeasuredEnergyStats template) { + final int arraySize = in.readInt(); // Check if any MeasuredEnergyStats exists on the parcel - if (in.readInt() == 0) return null; + if (arraySize == 0) return null; if (template == null) { - // Nothing supported now. Create placeholder object just to consume the parcel data. - final MeasuredEnergyStats mes = new MeasuredEnergyStats(); + // Nothing supported anymore. Create placeholder object just to consume the parcel data. + final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize); + mes.readSummaryFromParcel(in, false); + return null; + } + + if (arraySize != template.getNumberOfIndices()) { + Slog.wtf(TAG, "Size of MeasuredEnergyStats parcel (" + arraySize + + ") does not match template (" + template.getNumberOfIndices() + ")."); + // Something is horribly wrong. Just consume the parcel and return null. + final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize); mes.readSummaryFromParcel(in, false); return null; } @@ -251,14 +330,17 @@ public class MeasuredEnergyStats { /** Returns true iff any of the buckets are supported and non-zero. */ private boolean containsInterestingData() { - for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { - if (mAccumulatedEnergiesMicroJoules[bucket] > 0) return true; + for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) { + if (mAccumulatedEnergiesMicroJoules[index] > 0) return true; } return false; } /** * Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0. + * + * Corresponding read performed by {@link #createAndReadSummaryFromParcel(Parcel)} + * and {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}. */ public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats, Parcel dest, boolean skipZero) { @@ -266,14 +348,15 @@ public class MeasuredEnergyStats { dest.writeInt(0); return; } - dest.writeInt(1); + dest.writeInt(stats.getNumberOfIndices()); stats.writeSummaryToParcel(dest, skipZero); } /** Reset accumulated energy. */ private void reset() { - for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { - setValueIfSupported(bucket, 0L); + final int numIndices = getNumberOfIndices(); + for (int index = 0; index < numIndices; index++) { + setValueIfSupported(index, 0L); } } @@ -282,33 +365,93 @@ public class MeasuredEnergyStats { if (stats != null) stats.reset(); } - /** If the bucket is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */ - private void setValueIfSupported(@EnergyBucket int bucket, long value) { - if (mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE) { - mAccumulatedEnergiesMicroJoules[bucket] = value; + /** If the index is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */ + private void setValueIfSupported(int index, long value) { + if (mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE) { + mAccumulatedEnergiesMicroJoules[index] = value; } } - /** Check if measuring the energy of the given bucket is supported by this device. */ - public boolean isEnergyBucketSupported(@EnergyBucket int bucket) { - return mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE; + /** + * Check if measuring the energy of the given bucket is supported by this device. + * @throws IllegalArgumentException if not a valid {@link StandardEnergyBucket}. + */ + public boolean isStandardBucketSupported(@StandardEnergyBucket int bucket) { + checkValidStandardBucket(bucket); + return isIndexSupported(bucket); + } + + private boolean isIndexSupported(int index) { + return mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE; + } + + /** Check if the supported energy buckets are precisely those given. */ + public boolean isSupportEqualTo( + @NonNull boolean[] queriedStandardBuckets, int numCustomBuckets) { + + final int numBuckets = getNumberOfIndices(); + // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just + // quantitatively, and treat as mismatch if so. + if (numBuckets != NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets) { + return false; + } + for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) { + if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) { + return false; + } + } + return true; } /** Dump debug data. */ public void dump(PrintWriter pw) { pw.println("Accumulated energy since last reset (microjoules):"); pw.print(" "); - for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { - pw.print(ENERGY_BUCKET_NAMES[bucket]); + for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) { + pw.print(getBucketName(index)); pw.print(" : "); - pw.print(mAccumulatedEnergiesMicroJoules[bucket]); - if (!isEnergyBucketSupported(bucket)) { + pw.print(mAccumulatedEnergiesMicroJoules[index]); + if (!isIndexSupported(index)) { pw.print(" (unsupported)"); } - if (bucket != NUMBER_ENERGY_BUCKETS - 1) { + if (index != mAccumulatedEnergiesMicroJoules.length - 1) { pw.print(", "); } } pw.println(); } + + /** + * If the index is a standard bucket, returns its name; otherwise returns its prefixed custom + * bucket number. + */ + private static String getBucketName(int index) { + if (isValidStandardBucket(index)) { + return DebugUtils.valueToString(MeasuredEnergyStats.class, "ENERGY_BUCKET_", index); + } + return "CUSTOM_" + indexToCustomBucket(index); + } + + private static int customBucketToIndex(int customBucket) { + return customBucket + NUMBER_STANDARD_ENERGY_BUCKETS; + } + + private static int indexToCustomBucket(int index) { + return index - NUMBER_STANDARD_ENERGY_BUCKETS; + } + + private static void checkValidStandardBucket(@StandardEnergyBucket int bucket) { + if (!isValidStandardBucket(bucket)) { + throw new IllegalArgumentException("Illegal StandardEnergyBucket " + bucket); + } + } + + private static boolean isValidStandardBucket(@StandardEnergyBucket int bucket) { + return bucket >= 0 && bucket < NUMBER_STANDARD_ENERGY_BUCKETS; + } + + private boolean isValidCustomBucket(int customBucket) { + return customBucket >= 0 + && customBucketToIndex(customBucket) < mAccumulatedEnergiesMicroJoules.length; + } } diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index bf74c8d82fa2..1687a78c9875 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -47,9 +47,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { setExternalStatsSyncLocked(new DummyExternalStatsSync()); - final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; - Arrays.fill(supportedBuckets, true); - mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedBuckets); + final boolean[] supportedStandardBuckets + = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS]; + Arrays.fill(supportedStandardBuckets, true); + mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, 2); // A no-op handler. mHandler = new Handler(Looper.getMainLooper()) { diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index c9c81ac51944..b9908f46c81c 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -21,10 +21,11 @@ import static android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE; import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE; import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON; import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER; -import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS; +import static com.android.internal.power.MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.os.Parcel; @@ -47,60 +48,77 @@ public class MeasuredEnergyStatsTest { @Test public void testConstruction() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; - - final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (supportedEnergyBuckets[i]) { - assertTrue(stats.isEnergyBucketSupported(i)); - assertEquals(0L, stats.getAccumulatedBucketEnergy(i)); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { + if (supportedStandardBuckets[i]) { + assertTrue(stats.isStandardBucketSupported(i)); + assertEquals(0L, stats.getAccumulatedStandardBucketEnergy(i)); } else { - assertFalse(stats.isEnergyBucketSupported(i)); - assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i)); + assertFalse(stats.isStandardBucketSupported(i)); + assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i)); } } + for (int i = 0; i < numCustomBuckets; i++) { + assertEquals(0L, stats.getAccumulatedCustomBucketEnergy(i)); + } } @Test public void testCreateFromTemplate() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; - - final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + stats.updateCustomBucket(0, 50, true); + stats.updateCustomBucket(1, 60, true); final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats); - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (supportedEnergyBuckets[i]) { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(0, newStats.getAccumulatedBucketEnergy(i)); + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { + if (supportedStandardBuckets[i]) { + assertTrue(newStats.isStandardBucketSupported(i)); + assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i)); } else { - assertFalse(newStats.isEnergyBucketSupported(i)); - assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); + assertFalse(newStats.isStandardBucketSupported(i)); + assertEquals(ENERGY_DATA_UNAVAILABLE, + newStats.getAccumulatedStandardBucketEnergy(i)); } } + for (int i = 0; i < numCustomBuckets; i++) { + assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(i)); + } } @Test public void testReadWriteParcel() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; - - final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + stats.updateCustomBucket(0, 50, true); + stats.updateCustomBucket(1, 60, true); final Parcel parcel = Parcel.obtain(); stats.writeToParcel(parcel); @@ -108,94 +126,127 @@ public class MeasuredEnergyStatsTest { parcel.setDataPosition(0); MeasuredEnergyStats newStats = new MeasuredEnergyStats(parcel); - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - assertEquals(stats.getAccumulatedBucketEnergy(i), - newStats.getAccumulatedBucketEnergy(i)); + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { + assertEquals(stats.getAccumulatedStandardBucketEnergy(i), + newStats.getAccumulatedStandardBucketEnergy(i)); + } + for (int i = 0; i < numCustomBuckets; i++) { + assertEquals(stats.getAccumulatedCustomBucketEnergy(i), + newStats.getAccumulatedCustomBucketEnergy(i)); } + assertEquals(ENERGY_DATA_UNAVAILABLE, + newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1)); parcel.recycle(); } @Test public void testCreateAndReadSummaryFromParcel() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; - - final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + stats.updateCustomBucket(0, 50, true); + stats.updateCustomBucket(1, 60, true); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); parcel.setDataPosition(0); MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel); - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - assertEquals(stats.isEnergyBucketSupported(i), - newStats.isEnergyBucketSupported(i)); - assertEquals(stats.getAccumulatedBucketEnergy(i), - newStats.getAccumulatedBucketEnergy(i)); + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { + assertEquals(stats.isStandardBucketSupported(i), + newStats.isStandardBucketSupported(i)); + assertEquals(stats.getAccumulatedStandardBucketEnergy(i), + newStats.getAccumulatedStandardBucketEnergy(i)); + } + for (int i = 0; i < numCustomBuckets; i++) { + assertEquals(stats.getAccumulatedCustomBucketEnergy(i), + newStats.getAccumulatedCustomBucketEnergy(i)); } + assertEquals(ENERGY_DATA_UNAVAILABLE, + newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1)); parcel.recycle(); } @Test public void testCreateAndReadSummaryFromParcel_existingTemplate() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; - - final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedEnergyBuckets); - template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - template.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats template + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + template.updateCustomBucket(0, 50, true); final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 7, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true); + stats.updateCustomBucket(0, 315, true); + stats.updateCustomBucket(1, 316, true); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); - final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false - final MeasuredEnergyStats newTemplate = new MeasuredEnergyStats(newSupportedEnergyBuckets); + final boolean[] newsupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true + newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false + final MeasuredEnergyStats newTemplate + = new MeasuredEnergyStats(newsupportedStandardBuckets, numCustomBuckets); parcel.setDataPosition(0); final MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate); - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (!newSupportedEnergyBuckets[i]) { - assertFalse(newStats.isEnergyBucketSupported(i)); - assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); - } else if (!supportedEnergyBuckets[i]) { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { + if (!newsupportedStandardBuckets[i]) { + assertFalse(newStats.isStandardBucketSupported(i)); + assertEquals(ENERGY_DATA_UNAVAILABLE, + newStats.getAccumulatedStandardBucketEnergy(i)); + } else if (!supportedStandardBuckets[i]) { + assertTrue(newStats.isStandardBucketSupported(i)); + assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i)); } else { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(stats.getAccumulatedBucketEnergy(i), - newStats.getAccumulatedBucketEnergy(i)); + assertTrue(newStats.isStandardBucketSupported(i)); + assertEquals(stats.getAccumulatedStandardBucketEnergy(i), + newStats.getAccumulatedStandardBucketEnergy(i)); } } + for (int i = 0; i < numCustomBuckets; i++) { + assertEquals(stats.getAccumulatedCustomBucketEnergy(i), + newStats.getAccumulatedCustomBucketEnergy(i)); + } + assertEquals(ENERGY_DATA_UNAVAILABLE, + newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1)); parcel.recycle(); } @Test public void testCreateAndReadSummaryFromParcel_skipZero() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - Arrays.fill(supportedEnergyBuckets, true); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + Arrays.fill(supportedStandardBuckets, true); - final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - // Accumulate energy in one bucket, the rest should be zero - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + // Accumulate energy in one bucket and one custom bucket, the rest should be zero + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); + stats.updateCustomBucket(1, 60, true); + // Let's try parcelling with including zeros final Parcel includeZerosParcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false); includeZerosParcel.setDataPosition(0); @@ -203,90 +254,180 @@ public class MeasuredEnergyStatsTest { MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel( includeZerosParcel); - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { if (i == ENERGY_BUCKET_SCREEN_ON) { - assertEquals(stats.isEnergyBucketSupported(i), - newStats.isEnergyBucketSupported(i)); - assertEquals(stats.getAccumulatedBucketEnergy(i), - newStats.getAccumulatedBucketEnergy(i)); + assertEquals(stats.isStandardBucketSupported(i), + newStats.isStandardBucketSupported(i)); + assertEquals(stats.getAccumulatedStandardBucketEnergy(i), + newStats.getAccumulatedStandardBucketEnergy(i)); } else { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); + assertTrue(newStats.isStandardBucketSupported(i)); + assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i)); } } + assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0)); + assertEquals(stats.getAccumulatedCustomBucketEnergy(1), + newStats.getAccumulatedCustomBucketEnergy(1)); includeZerosParcel.recycle(); + // Now let's try parcelling with skipping zeros final Parcel skipZerosParcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true); skipZerosParcel.setDataPosition(0); newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel); - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { if (i == ENERGY_BUCKET_SCREEN_ON) { - assertEquals(stats.isEnergyBucketSupported(i), - newStats.isEnergyBucketSupported(i)); - assertEquals(stats.getAccumulatedBucketEnergy(i), - newStats.getAccumulatedBucketEnergy(i)); + assertEquals(stats.isStandardBucketSupported(i), + newStats.isStandardBucketSupported(i)); + assertEquals(stats.getAccumulatedStandardBucketEnergy(i), + newStats.getAccumulatedStandardBucketEnergy(i)); } else { - assertFalse(newStats.isEnergyBucketSupported(i)); - assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); + assertFalse(newStats.isStandardBucketSupported(i)); + assertEquals(ENERGY_DATA_UNAVAILABLE, + newStats.getAccumulatedStandardBucketEnergy(i)); } } + assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0)); + assertEquals(stats.getAccumulatedCustomBucketEnergy(1), + newStats.getAccumulatedCustomBucketEnergy(1)); skipZerosParcel.recycle(); } @Test + public void testCreateAndReadSummaryFromParcel_nullTemplate() { + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + stats.updateCustomBucket(0, 50, true); + stats.updateCustomBucket(1, 60, true); + + final Parcel parcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); + parcel.setDataPosition(0); + + MeasuredEnergyStats newStats + = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, null); + assertNull(newStats); + parcel.recycle(); + } + + @Test + public void testCreateAndReadSummaryFromParcel_boring() { + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats template + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + template.updateCustomBucket(0, 50, true); + + final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true); + + final Parcel parcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); + + final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true + newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false + final MeasuredEnergyStats newTemplate + = new MeasuredEnergyStats(newSupportedStandardBuckets, numCustomBuckets); + parcel.setDataPosition(0); + + final MeasuredEnergyStats newStats = + MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate); + // The only non-0 entry in stats is no longer supported, so now there's no interesting data. + assertNull(newStats); + assertEquals("Parcel was not properly consumed", 0, parcel.dataAvail()); + parcel.recycle(); + } + + @Test public void testUpdateBucket() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; - - final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - - assertEquals(15, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + + stats.updateCustomBucket(0, 50, true); + stats.updateCustomBucket(1, 60, true); + stats.updateCustomBucket(0, 3, true); + + assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); assertEquals(ENERGY_DATA_UNAVAILABLE, - stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE)); - assertEquals(40, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER)); + stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE)); + assertEquals(40, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER)); + assertEquals(50 + 3, stats.getAccumulatedCustomBucketEnergy(0)); + assertEquals(60, stats.getAccumulatedCustomBucketEnergy(1)); } @Test public void testReset() { - final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; - supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; - - final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS]; + final int numCustomBuckets = 2; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; + supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; + + final MeasuredEnergyStats stats + = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + stats.updateCustomBucket(0, 50, true); + stats.updateCustomBucket(1, 60, true); MeasuredEnergyStats.resetIfNotNull(stats); // All energy should be reset to 0 - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (supportedEnergyBuckets[i]) { - assertTrue(stats.isEnergyBucketSupported(i)); - assertEquals(0, stats.getAccumulatedBucketEnergy(i)); + for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) { + if (supportedStandardBuckets[i]) { + assertTrue(stats.isStandardBucketSupported(i)); + assertEquals(0, stats.getAccumulatedStandardBucketEnergy(i)); } else { - assertFalse(stats.isEnergyBucketSupported(i)); - assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i)); + assertFalse(stats.isStandardBucketSupported(i)); + assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i)); } } + for (int i = 0; i < numCustomBuckets; i++) { + assertEquals(0, stats.getAccumulatedCustomBucketEnergy(i)); + } // Values should increase as usual. - stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 70, true); - assertEquals(70L, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true); + assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); + + stats.updateCustomBucket(1, 12, true); + assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1)); } /** Test that states are mapped to the expected energy buckets. Beware of mapping changes. */ @Test - public void testEnergyBucketMapping() { + public void testStandardBucketMapping() { int exp; exp = ENERGY_BUCKET_SCREEN_ON; diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 170c34d2e859..c6947c2d9525 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -207,12 +207,13 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if (mPowerStatsInternal != null) { populateEnergyConsumerSubsystemMapsLocked(); final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData(); - final boolean[] supportedBuckets = getSupportedEnergyBuckets( - initialMeasuredEnergies); mMeasuredEnergySnapshot = initialMeasuredEnergies == null ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies); + final boolean[] supportedStdBuckets + = getSupportedEnergyBuckets(initialMeasuredEnergies); + final int numCustomBuckets = 0; // TODO: Get this from initialMeasuredEnergies synchronized (mStats) { - mStats.initMeasuredEnergyStatsLocked(supportedBuckets); + mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets); } } } @@ -740,15 +741,16 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { /** * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to - * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s. + * their corresponding {@link MeasuredEnergyStats.StandardEnergyBucket}s. + * Does not include custom energy buckets (which are always, by definition, supported). * - * @return array with true for index i if energy bucket i is supported. + * @return array with true for index i if standard energy bucket i is supported. */ private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) { if (energyArray == null) { return null; } - final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; + final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS]; final int size = energyArray.size(); for (int energyIdx = 0; energyIdx < size; energyIdx++) { switch (energyArray.getSubsystem(energyIdx)) { |