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_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());

View File

@@ -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;
}
}

View File

@@ -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()) {

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_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 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(supportedEnergyBuckets);
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
if (supportedEnergyBuckets[i]) {
assertTrue(stats.isEnergyBucketSupported(i));
assertEquals(0L, stats.getAccumulatedBucketEnergy(i));
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 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(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 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 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(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 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 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(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 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 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(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 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 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(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);
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);
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,
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 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(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 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;

View File

@@ -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)) {