summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java63
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java281
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java7
-rw-r--r--core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java407
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java14
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)) {