Merge "MeasuredEnergyStats supports custom energy buckets" into sc-dev

This commit is contained in:
Adam Bookatz
2021-02-04 08:38:15 +00:00
committed by Android (Google) Code Review
5 changed files with 520 additions and 234 deletions

View File

@@ -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_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_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.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; 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.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.power.MeasuredEnergyStats; 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.ArrayUtils;
import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.FrameworkStatsLog;
@@ -173,7 +171,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS' private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version // 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 // The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -7161,8 +7159,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (mGlobalMeasuredEnergyStats == null) { if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE; return ENERGY_DATA_UNAVAILABLE;
} }
return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy( return mGlobalMeasuredEnergyStats
MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON); .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
} }
@Override @Override
@@ -7170,8 +7168,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (mGlobalMeasuredEnergyStats == null) { if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE; return ENERGY_DATA_UNAVAILABLE;
} }
return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy( return mGlobalMeasuredEnergyStats
MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE); .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
} }
@Override public long getStartClockTime() { @Override public long getStartClockTime() {
@@ -7935,27 +7933,27 @@ public class BatteryStatsImpl extends BatteryStats {
return mUidMeasuredEnergyStats; return mUidMeasuredEnergyStats;
} }
/** Adds the given energy to the given energy bucket for this uid. */ /** Adds the given energy to the given standard energy bucket for this uid. */
private void addEnergyToEnergyBucketLocked(long energyDeltaUJ, private void addEnergyToStandardBucketLocked(long energyDeltaUJ,
@MeasuredEnergyStats.EnergyBucket int energyBucket, boolean accumulate) { @StandardEnergyBucket int energyBucket, boolean accumulate) {
getOrCreateMeasuredEnergyStatsLocked() getOrCreateMeasuredEnergyStatsLocked()
.updateBucket(energyBucket, energyDeltaUJ, accumulate); .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
} }
/** /**
* Returns the energy used by this uid for an energy bucket of interest. * Returns the energy used by this uid for a standard energy bucket of interest.
* @param bucket energy bucket of interest * @param bucket standard energy bucket of interest
* @return energy (in microjoules) used by this uid for this energy bucket * @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 if (mBsi.mGlobalMeasuredEnergyStats == null
|| !mBsi.mGlobalMeasuredEnergyStats.isEnergyBucketSupported(bucket)) { || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
return ENERGY_DATA_UNAVAILABLE; return ENERGY_DATA_UNAVAILABLE;
} }
if (mUidMeasuredEnergyStats == null) { if (mUidMeasuredEnergyStats == null) {
return 0L; // It is supported, but was never filled, so it must be 0 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; return;
} }
final @EnergyBucket int energyBucket = final @StandardEnergyBucket int energyBucket =
MeasuredEnergyStats.getDisplayEnergyBucket(mScreenStateAtLastEnergyMeasurement); MeasuredEnergyStats.getDisplayEnergyBucket(mScreenStateAtLastEnergyMeasurement);
mScreenStateAtLastEnergyMeasurement = screenState; mScreenStateAtLastEnergyMeasurement = screenState;
@@ -12415,7 +12413,7 @@ public class BatteryStatsImpl extends BatteryStats {
return; return;
} }
mGlobalMeasuredEnergyStats.updateBucket(energyBucket, energyUJ, true); mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true);
// Now we blame individual apps, but only if the display was ON. // Now we blame individual apps, but only if the display was ON.
if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) { if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) {
@@ -12453,7 +12451,7 @@ public class BatteryStatsImpl extends BatteryStats {
final long appDisplayEnergyMJ = final long appDisplayEnergyMJ =
(totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2)) (totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2))
/ totalFgTimeMs; / 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 // To mitigate round-off errors, remove this app from numerator & denominator totals
totalDisplayEnergyMJ -= appDisplayEnergyMJ; totalDisplayEnergyMJ -= appDisplayEnergyMJ;
@@ -14138,33 +14136,34 @@ public class BatteryStatsImpl extends BatteryStats {
/** /**
* Initialize the measured energy stats data structures. * 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") @GuardedBy("this")
public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) { public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
int numCustomBuckets) {
boolean supportedBucketMismatch = false; boolean supportedBucketMismatch = false;
mScreenStateAtLastEnergyMeasurement = mScreenState; mScreenStateAtLastEnergyMeasurement = mScreenState;
if (supportedEnergyBuckets == null) { if (supportedStandardBuckets == null) {
if (mGlobalMeasuredEnergyStats != null) { if (mGlobalMeasuredEnergyStats != null) {
// Measured energy buckets no longer supported, wipe out the existing data. // Measured energy buckets no longer supported, wipe out the existing data.
supportedBucketMismatch = true; supportedBucketMismatch = true;
} }
} else if (mGlobalMeasuredEnergyStats == null) { } else if (mGlobalMeasuredEnergyStats == null) {
mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets); mGlobalMeasuredEnergyStats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
return; return;
} else { } else {
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo(
if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i) supportedStandardBuckets, numCustomBuckets);
!= supportedEnergyBuckets[i]) {
mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
supportedBucketMismatch = true;
break;
}
}
} }
if (supportedBucketMismatch) { if (supportedBucketMismatch) {
mGlobalMeasuredEnergyStats = supportedStandardBuckets == null ?
null : new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
// Supported energy buckets changed since last boot. // Supported energy buckets changed since last boot.
// Existing data is no longer reliable. // Existing data is no longer reliable.
resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime()); resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime());

View File

@@ -20,8 +20,10 @@ package com.android.internal.power;
import static android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE; import static android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.os.Parcel; import android.os.Parcel;
import android.util.DebugUtils;
import android.util.Slog; import android.util.Slog;
import android.view.Display; import android.view.Display;
@@ -33,7 +35,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; 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 * 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. * 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 { public class MeasuredEnergyStats {
private static final String TAG = "MeasuredEnergyStats"; private static final String TAG = "MeasuredEnergyStats";
// Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if energy // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} MUST be updated if standard
// bucket integers are modified. // energy bucket integers are modified/added/removed.
public static final int ENERGY_BUCKET_UNKNOWN = -1; public static final int ENERGY_BUCKET_UNKNOWN = -1;
public static final int ENERGY_BUCKET_SCREEN_ON = 0; 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_DOZE = 1;
public static final int ENERGY_BUCKET_SCREEN_OTHER = 2; public static final int ENERGY_BUCKET_SCREEN_OTHER = 2;
public static final int NUMBER_ENERGY_BUCKETS = 3; public static final int NUMBER_STANDARD_ENERGY_BUCKETS = 3; // Buckets above this are custom.
private static final String[] ENERGY_BUCKET_NAMES =
{"screen-on", "screen-doze", "screen-other"};
@IntDef(prefix = {"ENERGY_BUCKET_"}, value = { @IntDef(prefix = {"ENERGY_BUCKET_"}, value = {
ENERGY_BUCKET_UNKNOWN, ENERGY_BUCKET_UNKNOWN,
@@ -59,28 +61,37 @@ public class MeasuredEnergyStats {
ENERGY_BUCKET_SCREEN_OTHER, ENERGY_BUCKET_SCREEN_OTHER,
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface EnergyBucket { public @interface StandardEnergyBucket {
} }
/** /**
* Total energy (in microjoules) that an {@link EnergyBucket} has accumulated since the last * Total energy (in microjoules) that an energy bucket (including both
* reset. Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs * {@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). * 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 * 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. * 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. * 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. // Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { // All custom buckets are, by definition, supported, so their values stay at 0.
if (!supportedEnergyBuckets[bucket]) { for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE; 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. * supported. This certainly does NOT produce an exact clone of the template.
*/ */
private MeasuredEnergyStats(MeasuredEnergyStats template) { private MeasuredEnergyStats(MeasuredEnergyStats template) {
final int numIndices = template.getNumberOfIndices();
mAccumulatedEnergiesMicroJoules = new long[numIndices];
// Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE. // Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { // All custom buckets are, by definition, supported, so their values stay at 0.
if (!template.isEnergyBucketSupported(bucket)) { for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE; if (!template.isIndexSupported(stdBucket)) {
mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE;
} }
} }
} }
@@ -108,18 +122,22 @@ public class MeasuredEnergyStats {
/** /**
* Constructor for creating a temp 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. */ /** Construct from parcel. */
public MeasuredEnergyStats(Parcel in) { public MeasuredEnergyStats(Parcel in) {
final int size = in.readInt();
mAccumulatedEnergiesMicroJoules = new long[size];
in.readLongArray(mAccumulatedEnergiesMicroJoules); in.readLongArray(mAccumulatedEnergiesMicroJoules);
} }
/** Write to parcel */ /** Write to parcel */
public void writeToParcel(Parcel out) { public void writeToParcel(Parcel out) {
out.writeInt(mAccumulatedEnergiesMicroJoules.length);
out.writeLongArray(mAccumulatedEnergiesMicroJoules); out.writeLongArray(mAccumulatedEnergiesMicroJoules);
} }
@@ -129,16 +147,18 @@ public class MeasuredEnergyStats {
* summary parcel was written. Availability has already been correctly set in the constructor. * 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 * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary
* parceling changes. * parceling changes.
*
* Corresponding write performed by {@link #writeSummaryToParcel(Parcel, boolean)}.
*/ */
private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) { private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) {
final int size = in.readInt(); final int numWrittenEntries = in.readInt();
for (int i = 0; i < size; i++) { for (int entry = 0; entry < numWrittenEntries; entry++) {
final int bucket = in.readInt(); final int index = in.readInt();
final long energyUJ = in.readLong(); final long energyUJ = in.readLong();
if (overwriteAvailability) { if (overwriteAvailability) {
mAccumulatedEnergiesMicroJoules[bucket] = energyUJ; mAccumulatedEnergiesMicroJoules[index] = energyUJ;
} else { } else {
setValueIfSupported(bucket, energyUJ); setValueIfSupported(index, energyUJ);
} }
} }
} }
@@ -146,52 +166,90 @@ public class MeasuredEnergyStats {
/** /**
* Write to summary parcel. * Write to summary parcel.
* Note: Measured subsystem availability may be different when the summary parcel is read. * 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) { private void writeSummaryToParcel(Parcel out, boolean skipZero) {
final int sizePos = out.dataPosition(); final int posOfNumWrittenEntries = out.dataPosition();
out.writeInt(0); out.writeInt(0);
int size = 0; int numWrittenEntries = 0;
// Write only the supported buckets with non-zero energy. // Write only the supported buckets (with non-zero energy, if applicable).
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
final long energy = mAccumulatedEnergiesMicroJoules[i]; final long energy = mAccumulatedEnergiesMicroJoules[index];
if (energy < 0) continue; if (energy < 0) continue;
if (energy == 0 && skipZero) continue; if (energy == 0 && skipZero) continue;
out.writeInt(i); out.writeInt(index);
out.writeLong(mAccumulatedEnergiesMicroJoules[i]); out.writeLong(mAccumulatedEnergiesMicroJoules[index]);
size++; numWrittenEntries++;
} }
final int currPos = out.dataPosition(); final int currPos = out.dataPosition();
out.setDataPosition(sizePos); out.setDataPosition(posOfNumWrittenEntries);
out.writeInt(size); out.writeInt(numWrittenEntries);
out.setDataPosition(currPos); out.setDataPosition(currPos);
} }
/** Updates the given bucket with the given energy iff accumulate is true. */ /** Get number of possible buckets, including both standard and custom ones. */
public void updateBucket(@EnergyBucket int bucket, long energyDeltaUJ, boolean accumulate) { 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 (accumulate) {
if (mAccumulatedEnergiesMicroJoules[bucket] >= 0L) { if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
mAccumulatedEnergiesMicroJoules[bucket] += energyDeltaUJ; mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
} else { } else {
Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket " Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
+ ENERGY_BUCKET_NAMES[bucket] + " whose value was " + getBucketName(index) + " whose value was "
+ mAccumulatedEnergiesMicroJoules[bucket]); + mAccumulatedEnergiesMicroJoules[index]);
} }
} }
} }
/** /**
* Return accumulated energy (in microjoules) for the given energy bucket since last reset. * Return accumulated energy (in microjoules) for a standard energy bucket since last reset.
* Returns {@link BatteryStats#ENERGY_DATA_UNAVAILABLE} if this energy data is unavailable. * 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]; 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)) { if (Display.isOnState(screenState)) {
return ENERGY_BUCKET_SCREEN_ON; return ENERGY_BUCKET_SCREEN_ON;
} }
@@ -204,15 +262,20 @@ public class MeasuredEnergyStats {
/** /**
* Create a MeasuredEnergyStats object from a summary parcel. * Create a MeasuredEnergyStats object from a summary parcel.
* *
* Corresponding write performed by
* {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}.
*
* @return a new MeasuredEnergyStats object as described. * @return a new MeasuredEnergyStats object as described.
* Returns null if the parcel indicates there is no data to populate. * Returns null if the parcel indicates there is no data to populate.
*/ */
public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) { public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) {
final int arraySize = in.readInt();
// Check if any MeasuredEnergyStats exists on the parcel // Check if any MeasuredEnergyStats exists on the parcel
if (in.readInt() == 0) return null; if (arraySize == 0) return null;
final MeasuredEnergyStats stats = final int numCustomBuckets = arraySize - NUMBER_STANDARD_ENERGY_BUCKETS;
new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]); final MeasuredEnergyStats stats = new MeasuredEnergyStats(
new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], numCustomBuckets);
stats.readSummaryFromParcel(in, true); stats.readSummaryFromParcel(in, true);
return stats; return stats;
} }
@@ -221,6 +284,12 @@ public class MeasuredEnergyStats {
* Create a MeasuredEnergyStats using the template to determine which buckets are supported, * Create a MeasuredEnergyStats using the template to determine which buckets are supported,
* and populate this new object from the given parcel. * 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. * @return a new MeasuredEnergyStats object as described.
* Returns null if the stats contain no non-0 information (such as if template is null * 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). * 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, public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in,
@Nullable MeasuredEnergyStats template) { @Nullable MeasuredEnergyStats template) {
final int arraySize = in.readInt();
// Check if any MeasuredEnergyStats exists on the parcel // Check if any MeasuredEnergyStats exists on the parcel
if (in.readInt() == 0) return null; if (arraySize == 0) return null;
if (template == null) { if (template == null) {
// Nothing supported now. Create placeholder object just to consume the parcel data. // Nothing supported anymore. Create placeholder object just to consume the parcel data.
final MeasuredEnergyStats mes = new MeasuredEnergyStats(); 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); mes.readSummaryFromParcel(in, false);
return null; return null;
} }
@@ -251,14 +330,17 @@ public class MeasuredEnergyStats {
/** Returns true iff any of the buckets are supported and non-zero. */ /** Returns true iff any of the buckets are supported and non-zero. */
private boolean containsInterestingData() { private boolean containsInterestingData() {
for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
if (mAccumulatedEnergiesMicroJoules[bucket] > 0) return true; if (mAccumulatedEnergiesMicroJoules[index] > 0) return true;
} }
return false; return false;
} }
/** /**
* Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0. * 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, public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats,
Parcel dest, boolean skipZero) { Parcel dest, boolean skipZero) {
@@ -266,14 +348,15 @@ public class MeasuredEnergyStats {
dest.writeInt(0); dest.writeInt(0);
return; return;
} }
dest.writeInt(1); dest.writeInt(stats.getNumberOfIndices());
stats.writeSummaryToParcel(dest, skipZero); stats.writeSummaryToParcel(dest, skipZero);
} }
/** Reset accumulated energy. */ /** Reset accumulated energy. */
private void reset() { private void reset() {
for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { final int numIndices = getNumberOfIndices();
setValueIfSupported(bucket, 0L); for (int index = 0; index < numIndices; index++) {
setValueIfSupported(index, 0L);
} }
} }
@@ -282,33 +365,93 @@ public class MeasuredEnergyStats {
if (stats != null) stats.reset(); if (stats != null) stats.reset();
} }
/** If the bucket is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */ /** If the index is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */
private void setValueIfSupported(@EnergyBucket int bucket, long value) { private void setValueIfSupported(int index, long value) {
if (mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE) { if (mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE) {
mAccumulatedEnergiesMicroJoules[bucket] = value; mAccumulatedEnergiesMicroJoules[index] = value;
} }
} }
/** Check if measuring the energy of the given bucket is supported by this device. */ /**
public boolean isEnergyBucketSupported(@EnergyBucket int bucket) { * Check if measuring the energy of the given bucket is supported by this device.
return mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE; * @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. */ /** Dump debug data. */
public void dump(PrintWriter pw) { public void dump(PrintWriter pw) {
pw.println("Accumulated energy since last reset (microjoules):"); pw.println("Accumulated energy since last reset (microjoules):");
pw.print(" "); pw.print(" ");
for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) { for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
pw.print(ENERGY_BUCKET_NAMES[bucket]); pw.print(getBucketName(index));
pw.print(" : "); pw.print(" : ");
pw.print(mAccumulatedEnergiesMicroJoules[bucket]); pw.print(mAccumulatedEnergiesMicroJoules[index]);
if (!isEnergyBucketSupported(bucket)) { if (!isIndexSupported(index)) {
pw.print(" (unsupported)"); pw.print(" (unsupported)");
} }
if (bucket != NUMBER_ENERGY_BUCKETS - 1) { if (index != mAccumulatedEnergiesMicroJoules.length - 1) {
pw.print(", "); pw.print(", ");
} }
} }
pw.println(); 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;
}
} }

View File

@@ -47,9 +47,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
setExternalStatsSyncLocked(new DummyExternalStatsSync()); setExternalStatsSyncLocked(new DummyExternalStatsSync());
final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets
Arrays.fill(supportedBuckets, true); = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedBuckets); Arrays.fill(supportedStandardBuckets, true);
mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, 2);
// A no-op handler. // A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) { mHandler = new Handler(Looper.getMainLooper()) {

View File

@@ -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_DOZE;
import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON; 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.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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import android.os.Parcel; import android.os.Parcel;
@@ -47,60 +48,77 @@ public class MeasuredEnergyStatsTest {
@Test @Test
public void testConstruction() { public void testConstruction() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; final int numCustomBuckets = 2;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (supportedEnergyBuckets[i]) { if (supportedStandardBuckets[i]) {
assertTrue(stats.isEnergyBucketSupported(i)); assertTrue(stats.isStandardBucketSupported(i));
assertEquals(0L, stats.getAccumulatedBucketEnergy(i)); assertEquals(0L, stats.getAccumulatedStandardBucketEnergy(i));
} else { } else {
assertFalse(stats.isEnergyBucketSupported(i)); assertFalse(stats.isStandardBucketSupported(i));
assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i)); assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i));
} }
} }
for (int i = 0; i < numCustomBuckets; i++) {
assertEquals(0L, stats.getAccumulatedCustomBucketEnergy(i));
}
} }
@Test @Test
public void testCreateFromTemplate() { public void testCreateFromTemplate() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; final int numCustomBuckets = 2;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats stats
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, 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); final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats);
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (supportedEnergyBuckets[i]) { if (supportedStandardBuckets[i]) {
assertTrue(newStats.isEnergyBucketSupported(i)); assertTrue(newStats.isStandardBucketSupported(i));
assertEquals(0, newStats.getAccumulatedBucketEnergy(i)); assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
} else { } else {
assertFalse(newStats.isEnergyBucketSupported(i)); assertFalse(newStats.isStandardBucketSupported(i));
assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); assertEquals(ENERGY_DATA_UNAVAILABLE,
newStats.getAccumulatedStandardBucketEnergy(i));
} }
} }
for (int i = 0; i < numCustomBuckets; i++) {
assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(i));
}
} }
@Test @Test
public void testReadWriteParcel() { public void testReadWriteParcel() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; final int numCustomBuckets = 2;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats stats
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, 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(); final Parcel parcel = Parcel.obtain();
stats.writeToParcel(parcel); stats.writeToParcel(parcel);
@@ -108,94 +126,127 @@ public class MeasuredEnergyStatsTest {
parcel.setDataPosition(0); parcel.setDataPosition(0);
MeasuredEnergyStats newStats = new MeasuredEnergyStats(parcel); MeasuredEnergyStats newStats = new MeasuredEnergyStats(parcel);
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
assertEquals(stats.getAccumulatedBucketEnergy(i), assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
newStats.getAccumulatedBucketEnergy(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(); parcel.recycle();
} }
@Test @Test
public void testCreateAndReadSummaryFromParcel() { public void testCreateAndReadSummaryFromParcel() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; final int numCustomBuckets = 2;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats stats
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, 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(); final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
parcel.setDataPosition(0); parcel.setDataPosition(0);
MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel); MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel);
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
assertEquals(stats.isEnergyBucketSupported(i), assertEquals(stats.isStandardBucketSupported(i),
newStats.isEnergyBucketSupported(i)); newStats.isStandardBucketSupported(i));
assertEquals(stats.getAccumulatedBucketEnergy(i), assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
newStats.getAccumulatedBucketEnergy(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(); parcel.recycle();
} }
@Test @Test
public void testCreateAndReadSummaryFromParcel_existingTemplate() { public void testCreateAndReadSummaryFromParcel_existingTemplate() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; final int numCustomBuckets = 2;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats template
template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
template.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, 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); final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 7, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
stats.updateCustomBucket(0, 315, true);
stats.updateCustomBucket(1, 316, true);
final Parcel parcel = Parcel.obtain(); final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] newsupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true
newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false
final MeasuredEnergyStats newTemplate = new MeasuredEnergyStats(newSupportedEnergyBuckets); final MeasuredEnergyStats newTemplate
= new MeasuredEnergyStats(newsupportedStandardBuckets, numCustomBuckets);
parcel.setDataPosition(0); parcel.setDataPosition(0);
final MeasuredEnergyStats newStats = final MeasuredEnergyStats newStats =
MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate); MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (!newSupportedEnergyBuckets[i]) { if (!newsupportedStandardBuckets[i]) {
assertFalse(newStats.isEnergyBucketSupported(i)); assertFalse(newStats.isStandardBucketSupported(i));
assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); assertEquals(ENERGY_DATA_UNAVAILABLE,
} else if (!supportedEnergyBuckets[i]) { newStats.getAccumulatedStandardBucketEnergy(i));
assertTrue(newStats.isEnergyBucketSupported(i)); } else if (!supportedStandardBuckets[i]) {
assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); assertTrue(newStats.isStandardBucketSupported(i));
assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
} else { } else {
assertTrue(newStats.isEnergyBucketSupported(i)); assertTrue(newStats.isStandardBucketSupported(i));
assertEquals(stats.getAccumulatedBucketEnergy(i), assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
newStats.getAccumulatedBucketEnergy(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(); parcel.recycle();
} }
@Test @Test
public void testCreateAndReadSummaryFromParcel_skipZero() { public void testCreateAndReadSummaryFromParcel_skipZero() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
Arrays.fill(supportedEnergyBuckets, true); final int numCustomBuckets = 2;
Arrays.fill(supportedStandardBuckets, true);
final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats stats
// Accumulate energy in one bucket, the rest should be zero = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); // 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(); final Parcel includeZerosParcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false); MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false);
includeZerosParcel.setDataPosition(0); includeZerosParcel.setDataPosition(0);
@@ -203,90 +254,180 @@ public class MeasuredEnergyStatsTest {
MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel( MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
includeZerosParcel); 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) { if (i == ENERGY_BUCKET_SCREEN_ON) {
assertEquals(stats.isEnergyBucketSupported(i), assertEquals(stats.isStandardBucketSupported(i),
newStats.isEnergyBucketSupported(i)); newStats.isStandardBucketSupported(i));
assertEquals(stats.getAccumulatedBucketEnergy(i), assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
newStats.getAccumulatedBucketEnergy(i)); newStats.getAccumulatedStandardBucketEnergy(i));
} else { } else {
assertTrue(newStats.isEnergyBucketSupported(i)); assertTrue(newStats.isStandardBucketSupported(i));
assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
} }
} }
assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
newStats.getAccumulatedCustomBucketEnergy(1));
includeZerosParcel.recycle(); includeZerosParcel.recycle();
// Now let's try parcelling with skipping zeros
final Parcel skipZerosParcel = Parcel.obtain(); final Parcel skipZerosParcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true); MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true);
skipZerosParcel.setDataPosition(0); skipZerosParcel.setDataPosition(0);
newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel); 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) { if (i == ENERGY_BUCKET_SCREEN_ON) {
assertEquals(stats.isEnergyBucketSupported(i), assertEquals(stats.isStandardBucketSupported(i),
newStats.isEnergyBucketSupported(i)); newStats.isStandardBucketSupported(i));
assertEquals(stats.getAccumulatedBucketEnergy(i), assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
newStats.getAccumulatedBucketEnergy(i)); newStats.getAccumulatedStandardBucketEnergy(i));
} else { } else {
assertFalse(newStats.isEnergyBucketSupported(i)); assertFalse(newStats.isStandardBucketSupported(i));
assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); assertEquals(ENERGY_DATA_UNAVAILABLE,
newStats.getAccumulatedStandardBucketEnergy(i));
} }
} }
assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
newStats.getAccumulatedCustomBucketEnergy(1));
skipZerosParcel.recycle(); 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 @Test
public void testUpdateBucket() { public void testUpdateBucket() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; final int numCustomBuckets = 2;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats stats
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
stats.updateBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
assertEquals(15, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); 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, assertEquals(ENERGY_DATA_UNAVAILABLE,
stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE)); stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE));
assertEquals(40, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER)); assertEquals(40, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER));
assertEquals(50 + 3, stats.getAccumulatedCustomBucketEnergy(0));
assertEquals(60, stats.getAccumulatedCustomBucketEnergy(1));
} }
@Test @Test
public void testReset() { public void testReset() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; final int numCustomBuckets = 2;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true; supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); final MeasuredEnergyStats stats
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, 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); MeasuredEnergyStats.resetIfNotNull(stats);
// All energy should be reset to 0 // All energy should be reset to 0
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (supportedEnergyBuckets[i]) { if (supportedStandardBuckets[i]) {
assertTrue(stats.isEnergyBucketSupported(i)); assertTrue(stats.isStandardBucketSupported(i));
assertEquals(0, stats.getAccumulatedBucketEnergy(i)); assertEquals(0, stats.getAccumulatedStandardBucketEnergy(i));
} else { } else {
assertFalse(stats.isEnergyBucketSupported(i)); assertFalse(stats.isStandardBucketSupported(i));
assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(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. // Values should increase as usual.
stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 70, true); stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
assertEquals(70L, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); 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 that states are mapped to the expected energy buckets. Beware of mapping changes. */
@Test @Test
public void testEnergyBucketMapping() { public void testStandardBucketMapping() {
int exp; int exp;
exp = ENERGY_BUCKET_SCREEN_ON; exp = ENERGY_BUCKET_SCREEN_ON;

View File

@@ -207,12 +207,13 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if (mPowerStatsInternal != null) { if (mPowerStatsInternal != null) {
populateEnergyConsumerSubsystemMapsLocked(); populateEnergyConsumerSubsystemMapsLocked();
final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData(); final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData();
final boolean[] supportedBuckets = getSupportedEnergyBuckets(
initialMeasuredEnergies);
mMeasuredEnergySnapshot = initialMeasuredEnergies == null mMeasuredEnergySnapshot = initialMeasuredEnergies == null
? null : new MeasuredEnergySnapshot(initialMeasuredEnergies); ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
final boolean[] supportedStdBuckets
= getSupportedEnergyBuckets(initialMeasuredEnergies);
final int numCustomBuckets = 0; // TODO: Get this from initialMeasuredEnergies
synchronized (mStats) { 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 * 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) { private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
if (energyArray == null) { if (energyArray == null) {
return 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(); final int size = energyArray.size();
for (int energyIdx = 0; energyIdx < size; energyIdx++) { for (int energyIdx = 0; energyIdx < size; energyIdx++) {
switch (energyArray.getSubsystem(energyIdx)) { switch (energyArray.getSubsystem(energyIdx)) {