summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Michael Wachenschwanz <mwachens@google.com> 2021-03-22 17:57:37 -0700
committer Michael Wachenschwanz <mwachens@google.com> 2021-03-23 18:40:35 -0700
commit27959e75dcf8bd041eafa6d1809f77a28c6ef9a2 (patch)
tree3f59988008498d4b19b1e161306cc310d8711844
parentc28c4ff016e3ada36b6d0223191fcdbfd2e15815 (diff)
Accumulate Mobile Radio and Gnss measured energy in BatteryStats
Enabled querying and processing Mobile Radio and Gnss energy consumption in BatteryStatsExternal. Attribute and accumulate their power deltas in BatteryStatsImpl Utilize measured power data in MobileRadioPowerCalculator and GnssPowerCaluclulator. Bug: 174818228 Test: atest GnssPowerCalculatorTest Test: atest MobileRadioPowerCalculatorTest Test: BatteryStatsExternalWorkerTest Change-Id: If062033ff3c8cff20842be2cd92b04f6cb981366
-rw-r--r--core/java/android/os/BatteryStats.java36
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java165
-rw-r--r--core/java/com/android/internal/os/GnssPowerCalculator.java28
-rw-r--r--core/java/com/android/internal/os/MobileRadioPowerCalculator.java102
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java6
-rw-r--r--core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java39
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java64
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java19
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java3
-rw-r--r--services/core/java/com/android/server/am/MeasuredEnergySnapshot.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java17
11 files changed, 449 insertions, 44 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fa6472ee4a79..2e843479963b 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1004,6 +1004,24 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getCpuMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the uid's GNSS usage, derived from
+ * on device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getGnssMeasuredBatteryConsumptionUC();
+
+ /**
+ * Returns the battery consumption (in microcoulombs) of the uid's radio usage, derived from
+ * on device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
+
+ /**
* Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
* derived from on device power measurement data.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
@@ -2548,6 +2566,24 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getCpuMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the GNSS, derived from on device power
+ * measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getGnssMeasuredBatteryConsumptionUC();
+
+ /**
+ * Returns the battery consumption (in microcoulombs) of the radio, derived from on device power
+ * measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
+
+ /**
* Returns the battery consumption (in microcoulombs) of the screen while on, derived from on
* device power measurement data.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7f8788529714..1fedfd6dbea5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -169,7 +169,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 195;
+ static final int VERSION = 196;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -1013,6 +1013,9 @@ public class BatteryStatsImpl extends BatteryStats {
@Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
/** Cpu Power calculator for attributing measured cpu charge consumption to uids */
@Nullable CpuPowerCalculator mCpuPowerCalculator = null;
+ /** Mobile Radio Power calculator for attributing measured radio charge consumption to uids */
+ @Nullable
+ MobileRadioPowerCalculator mMobileRadioPowerCalculator = null;
/** Wifi Power calculator for attributing measured wifi charge consumption to uids */
@Nullable WifiPowerCalculator mWifiPowerCalculator = null;
@@ -6981,6 +6984,16 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public long getGnssMeasuredBatteryConsumptionUC() {
+ return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
+ }
+
+ @Override
+ public long getMobileRadioMeasuredBatteryConsumptionUC() {
+ return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
+ }
+
+ @Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@@ -7840,6 +7853,16 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public long getGnssMeasuredBatteryConsumptionUC() {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
+ }
+
+ @Override
+ public long getMobileRadioMeasuredBatteryConsumptionUC() {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
+ }
+
+ @Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@@ -7878,6 +7901,27 @@ public class BatteryStatsImpl extends BatteryStats {
return (topTimeUs < fgTimeUs) ? topTimeUs : fgTimeUs;
}
+
+ /**
+ * Gets the uid's time spent using the GNSS since last marked. Also sets the mark time for
+ * the GNSS timer.
+ */
+ private long markGnssTimeUs(long elapsedRealtimeMs) {
+ final Sensor sensor = mSensorStats.get(Sensor.GPS);
+ if (sensor == null) {
+ return 0;
+ }
+
+ final StopwatchTimer timer = sensor.mTimer;
+ if (timer == null) {
+ return 0;
+ }
+
+ final long gnssTimeUs = timer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000);
+ timer.setMark(elapsedRealtimeMs);
+ return gnssTimeUs;
+ }
+
public StopwatchTimer createAudioTurnedOnTimerLocked() {
if (mAudioTurnedOnTimer == null) {
mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, AUDIO_TURNED_ON,
@@ -11821,7 +11865,7 @@ public class BatteryStatsImpl extends BatteryStats {
* Distribute Cell radio energy info and network traffic to apps.
*/
public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
- long elapsedRealtimeMs, long uptimeMs) {
+ final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
@@ -11852,6 +11896,16 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
+ final SparseDoubleArray uidEstimatedConsumptionMah;
+ if (consumedChargeUC > 0 && mMobileRadioPowerCalculator != null
+ && mGlobalMeasuredEnergyStats != null) {
+ mGlobalMeasuredEnergyStats.updateStandardBucket(
+ MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC);
+ uidEstimatedConsumptionMah = new SparseDoubleArray();
+ } else {
+ uidEstimatedConsumptionMah = null;
+ }
+
if (deltaInfo != null) {
mHasModemReporting = true;
mModemActivity.getIdleTimeCounter().addCountLocked(
@@ -11896,7 +11950,7 @@ public class BatteryStatsImpl extends BatteryStats {
mTmpRailStats.resetCellularTotalEnergyUsed();
}
}
- long radioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
+ long totalAppRadioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000);
mMobileRadioActivePerAppTimer.setMark(elapsedRealtimeMs);
@@ -11956,12 +12010,21 @@ public class BatteryStatsImpl extends BatteryStats {
// Distribute total radio active time in to this app.
final long appPackets = entry.rxPackets + entry.txPackets;
- final long appRadioTimeUs = (radioTimeUs * appPackets) / totalPackets;
+ final long appRadioTimeUs =
+ (totalAppRadioTimeUs * appPackets) / totalPackets;
u.noteMobileRadioActiveTimeLocked(appRadioTimeUs);
+ // Distribute measured mobile radio charge consumption based on app radio
+ // active time
+ if (uidEstimatedConsumptionMah != null) {
+ uidEstimatedConsumptionMah.add(u.getUid(),
+ mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
+ appRadioTimeUs / 1000));
+ }
+
// Remove this app from the totals, so that we don't lose any time
// due to rounding.
- radioTimeUs -= appRadioTimeUs;
+ totalAppRadioTimeUs -= appRadioTimeUs;
totalPackets -= appPackets;
if (deltaInfo != null) {
@@ -11986,12 +12049,51 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- if (radioTimeUs > 0) {
+ if (totalAppRadioTimeUs > 0) {
// Whoops, there is some radio time we can't blame on an app!
- mMobileRadioActiveUnknownTime.addCountLocked(radioTimeUs);
+ mMobileRadioActiveUnknownTime.addCountLocked(totalAppRadioTimeUs);
mMobileRadioActiveUnknownCount.addCountLocked(1);
}
+
+ // Update the MeasuredEnergyStats information.
+ if (uidEstimatedConsumptionMah != null) {
+ double totalEstimatedConsumptionMah = 0.0;
+
+ // Estimate total active radio power consumption since last mark.
+ final long totalRadioTimeMs = mMobileRadioActiveTimer.getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000) / 1000;
+ mMobileRadioActiveTimer.setMark(elapsedRealtimeMs);
+ totalEstimatedConsumptionMah +=
+ mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
+ totalRadioTimeMs);
+
+ // Estimate idle power consumption at each signal strength level
+ final int numSignalStrengthLevels = mPhoneSignalStrengthsTimer.length;
+ for (int strengthLevel = 0; strengthLevel < numSignalStrengthLevels;
+ strengthLevel++) {
+ final long strengthLevelDurationMs =
+ mPhoneSignalStrengthsTimer[strengthLevel].getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000) / 1000;
+ mPhoneSignalStrengthsTimer[strengthLevel].setMark(elapsedRealtimeMs);
+
+ totalEstimatedConsumptionMah +=
+ mMobileRadioPowerCalculator.calcIdlePowerAtSignalStrengthMah(
+ strengthLevelDurationMs, strengthLevel);
+ }
+
+ // Estimate total active radio power consumption since last mark.
+ final long scanTimeMs = mPhoneSignalScanningTimer.getTimeSinceMarkLocked(
+ elapsedRealtimeMs * 1000) / 1000;
+ mPhoneSignalScanningTimer.setMark(elapsedRealtimeMs);
+ totalEstimatedConsumptionMah +=
+ mMobileRadioPowerCalculator.calcScanTimePowerMah(scanTimeMs);
+
+ distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+ consumedChargeUC, uidEstimatedConsumptionMah,
+ totalEstimatedConsumptionMah);
+ }
+
mNetworkStatsPool.release(delta);
delta = null;
}
@@ -12451,7 +12553,7 @@ public class BatteryStatsImpl extends BatteryStats {
// 'double counted' and will simply exceed the realtime that elapsed.
// If multidisplay becomes a reality, this is probably more reasonable than pooling.
- // On the first pass, collect total time since mark so that we can normalize power.
+ // Collect total time since mark so that we can normalize power.
final SparseDoubleArray fgTimeUsArray = new SparseDoubleArray();
final long elapsedRealtimeUs = elapsedRealtimeMs * 1000;
// TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
@@ -12466,6 +12568,50 @@ public class BatteryStatsImpl extends BatteryStats {
}
/**
+ * Accumulate GNSS charge consumption and distribute it to the correct state and the apps.
+ *
+ * @param chargeUC amount of charge (microcoulombs) used by GNSS since this was last called.
+ */
+ @GuardedBy("this")
+ public void updateGnssMeasuredEnergyStatsLocked(long chargeUC, long elapsedRealtimeMs) {
+ if (DEBUG_ENERGY) Slog.d(TAG, "Updating gnss stats: " + chargeUC);
+ if (mGlobalMeasuredEnergyStats == null) {
+ return;
+ }
+
+ if (!mOnBatteryInternal || chargeUC <= 0) {
+ // There's nothing further to update.
+ return;
+ }
+ if (mIgnoreNextExternalStats) {
+ // Although under ordinary resets we won't get here, and typically a new sync will
+ // happen right after the reset, strictly speaking we need to set all mark times to now.
+ final int uidStatsSize = mUidStats.size();
+ for (int i = 0; i < uidStatsSize; i++) {
+ final Uid uid = mUidStats.valueAt(i);
+ uid.markGnssTimeUs(elapsedRealtimeMs);
+ }
+ return;
+ }
+
+ mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_GNSS,
+ chargeUC);
+
+ // Collect the per uid time since mark so that we can normalize power.
+ final SparseDoubleArray gnssTimeUsArray = new SparseDoubleArray();
+ // TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
+ final int uidStatsSize = mUidStats.size();
+ for (int i = 0; i < uidStatsSize; i++) {
+ final Uid uid = mUidStats.valueAt(i);
+ final long gnssTimeUs = uid.markGnssTimeUs(elapsedRealtimeMs);
+ if (gnssTimeUs == 0) continue;
+ gnssTimeUsArray.put(uid.getUid(), (double) gnssTimeUs);
+ }
+ distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_GNSS, chargeUC,
+ gnssTimeUsArray, 0);
+ }
+
+ /**
* Accumulate Custom power bucket charge, globally and for each app.
*
* @param totalChargeUC charge (microcoulombs) used for this bucket since this was last called.
@@ -14467,6 +14613,9 @@ public class BatteryStatsImpl extends BatteryStats {
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) {
mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
}
+ if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO]) {
+ mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile);
+ }
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) {
mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
}
diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java
index df25cdaee17f..97c4fd8b7b7a 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -61,7 +61,17 @@ public class GnssPowerCalculator extends PowerCalculator {
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
double averageGnssPowerMa) {
final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
- double powerMah = computePower(durationMs, averageGnssPowerMa);
+
+ final long measuredChargeUC = u.getGnssMeasuredBatteryConsumptionUC();
+ final boolean isMeasuredPowerAvailable = !query.shouldForceUsePowerProfileModel()
+ && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+
+ final double powerMah;
+ if (isMeasuredPowerAvailable) {
+ powerMah = uCtoMah(measuredChargeUC);
+ } else {
+ powerMah = computePower(durationMs, averageGnssPowerMa);
+ }
app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah);
}
@@ -73,15 +83,25 @@ public class GnssPowerCalculator extends PowerCalculator {
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
- calculateApp(app, app.uidObj, rawRealtimeUs, statsType, averageGnssPowerMa);
+ calculateApp(app, app.uidObj, rawRealtimeUs, statsType, averageGnssPowerMa, false);
}
}
}
protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- int statsType, double averageGnssPowerMa) {
+ int statsType, double averageGnssPowerMa, boolean shouldForceUsePowerProfileModel) {
final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
- double powerMah = computePower(durationMs, averageGnssPowerMa);
+
+ final long measuredChargeUC = u.getGnssMeasuredBatteryConsumptionUC();
+ final boolean isMeasuredPowerAvailable = shouldForceUsePowerProfileModel
+ && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+
+ final double powerMah;
+ if (isMeasuredPowerAvailable) {
+ powerMah = uCtoMah(measuredChargeUC);
+ } else {
+ powerMah = computePower(durationMs, averageGnssPowerMa);
+ }
app.gpsTimeMs = durationMs;
app.gpsPowerMah = powerMah;
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 22001d4ffbb7..498e1f2931a0 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -96,10 +96,12 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
final BatteryStats.Uid uid = app.getBatteryStatsUid();
- calculateApp(app, uid, powerPerPacketMah, total);
+ calculateApp(app, uid, powerPerPacketMah, total,
+ query.shouldForceUsePowerProfileModel());
}
- calculateRemaining(total, batteryStats, rawRealtimeUs);
+ calculateRemaining(total, batteryStats, rawRealtimeUs,
+ query.shouldForceUsePowerProfileModel());
if (total.powerMah != 0) {
builder.getOrCreateSystemBatteryConsumerBuilder(
@@ -111,11 +113,13 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
- double powerPerPacketMah, PowerAndDuration total) {
+ double powerPerPacketMah, PowerAndDuration total,
+ boolean shouldForceUsePowerProfileModel) {
final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
total.totalAppDurationMs += radioActiveDurationMs;
- final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs);
+ final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs,
+ shouldForceUsePowerProfileModel);
app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
radioActiveDurationMs)
@@ -132,12 +136,12 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
final BatteryStats.Uid u = app.uidObj;
- calculateApp(app, u, statsType, mobilePowerPerPacket, total);
+ calculateApp(app, u, statsType, mobilePowerPerPacket, total, false);
}
}
BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
- calculateRemaining(total, batteryStats, rawRealtimeUs);
+ calculateRemaining(total, batteryStats, rawRealtimeUs, false);
if (total.powerMah != 0) {
if (total.signalDurationMs != 0) {
radio.noCoveragePercent =
@@ -154,9 +158,12 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
- double powerPerPacketMah, PowerAndDuration total) {
+ double powerPerPacketMah, PowerAndDuration total,
+ boolean shouldForceUsePowerProfileModel) {
app.mobileActive = calculateDuration(u, statsType);
- app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive);
+
+ app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive,
+ shouldForceUsePowerProfileModel);
total.totalAppDurationMs += app.mobileActive;
// Add cost of mobile traffic.
@@ -183,11 +190,19 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private double calculatePower(BatteryStats.Uid u, double powerPerPacketMah,
- long radioActiveDurationMs) {
+ long radioActiveDurationMs, boolean shouldForceUsePowerProfileModel) {
+
+ final long measuredChargeUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
+ final boolean isMeasuredPowerAvailable = !shouldForceUsePowerProfileModel
+ && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+ if (isMeasuredPowerAvailable) {
+ return uCtoMah(measuredChargeUC);
+ }
+
if (radioActiveDurationMs > 0) {
// We are tracking when the radio is up, so can use the active time to
// determine power use.
- return mActivePowerEstimator.calculatePower(radioActiveDurationMs);
+ return calcPowerFromRadioActiveDurationMah(radioActiveDurationMs);
} else {
// We are not tracking when the radio is up, so must approximate power use
// based on the number of packets.
@@ -202,18 +217,29 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private void calculateRemaining(MobileRadioPowerCalculator.PowerAndDuration total,
- BatteryStats batteryStats, long rawRealtimeUs) {
+ BatteryStats batteryStats, long rawRealtimeUs,
+ boolean shouldForceUsePowerProfileModel) {
long signalTimeMs = 0;
double powerMah = 0;
+
+ final long measuredChargeUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
+ final boolean isMeasuredPowerAvailable = !shouldForceUsePowerProfileModel
+ && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE;
+ if (isMeasuredPowerAvailable) {
+ powerMah = uCtoMah(measuredChargeUC);
+ }
+
for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED) / 1000;
- final double p = mIdlePowerEstimators[i].calculatePower(strengthTimeMs);
- if (DEBUG && p != 0) {
- Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
- + formatCharge(p));
+ if (!isMeasuredPowerAvailable) {
+ final double p = calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i);
+ if (DEBUG && p != 0) {
+ Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
+ + formatCharge(p));
+ }
+ powerMah += p;
}
- powerMah += p;
signalTimeMs += strengthTimeMs;
if (i == 0) {
total.noCoverageDurationMs = strengthTimeMs;
@@ -222,16 +248,21 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED) / 1000;
- final double p = mScanPowerEstimator.calculatePower(scanningTimeMs);
- if (DEBUG && p != 0) {
- Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(p));
- }
- powerMah += p;
long radioActiveTimeMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED) / 1000;
long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs;
- if (remainingActiveTimeMs > 0) {
- powerMah += mActivePowerEstimator.calculatePower(remainingActiveTimeMs);
+
+ if (!isMeasuredPowerAvailable) {
+ final double p = calcScanTimePowerMah(scanningTimeMs);
+ if (DEBUG && p != 0) {
+ Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(
+ p));
+ }
+ powerMah += p;
+
+ if (remainingActiveTimeMs > 0) {
+ powerMah += calcPowerFromRadioActiveDurationMah(remainingActiveTimeMs);
+ }
}
total.durationMs = radioActiveTimeMs;
total.powerMah = powerMah;
@@ -239,12 +270,35 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
/**
+ * Calculates active radio power consumption (in milliamp-hours) from active radio duration.
+ */
+ public double calcPowerFromRadioActiveDurationMah(long radioActiveDurationMs) {
+ return mActivePowerEstimator.calculatePower(radioActiveDurationMs);
+ }
+
+ /**
+ * Calculates idle radio power consumption (in milliamp-hours) for time spent at a cell signal
+ * strength level.
+ * see {@link CellSignalStrength#getNumSignalStrengthLevels()}
+ */
+ public double calcIdlePowerAtSignalStrengthMah(long strengthTimeMs, int strengthLevel) {
+ return mIdlePowerEstimators[strengthLevel].calculatePower(strengthTimeMs);
+ }
+
+ /**
+ * Calculates radio scan power consumption (in milliamp-hours) from scan time.
+ */
+ public double calcScanTimePowerMah(long scanningTimeMs) {
+ return mScanPowerEstimator.calculatePower(scanningTimeMs);
+ }
+
+ /**
* Return estimated power (in mAh) of sending or receiving a packet with the mobile radio.
*/
private double getMobilePowerPerPacket(BatteryStats stats, long rawRealtimeUs, int statsType) {
final long radioDataUptimeMs =
stats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
- final double mobilePower = mActivePowerEstimator.calculatePower(radioDataUptimeMs);
+ final double mobilePower = calcPowerFromRadioActiveDurationMah(radioDataUptimeMs);
final long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
statsType);
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 845b3e501c08..00a0627d068c 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -54,7 +54,9 @@ public class MeasuredEnergyStats {
public static final int POWER_BUCKET_CPU = 3;
public static final int POWER_BUCKET_WIFI = 4;
public static final int POWER_BUCKET_BLUETOOTH = 5;
- public static final int NUMBER_STANDARD_POWER_BUCKETS = 6; // Buckets above this are custom.
+ public static final int POWER_BUCKET_GNSS = 6;
+ public static final int POWER_BUCKET_MOBILE_RADIO = 7;
+ public static final int NUMBER_STANDARD_POWER_BUCKETS = 8; // Buckets above this are custom.
@IntDef(prefix = {"POWER_BUCKET_"}, value = {
POWER_BUCKET_UNKNOWN,
@@ -64,6 +66,8 @@ public class MeasuredEnergyStats {
POWER_BUCKET_CPU,
POWER_BUCKET_WIFI,
POWER_BUCKET_BLUETOOTH,
+ POWER_BUCKET_GNSS,
+ POWER_BUCKET_MOBILE_RADIO,
})
@Retention(RetentionPolicy.SOURCE)
public @interface StandardPowerBucket {
diff --git a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
index bd7f1e2c3410..eed61cb0afe7 100644
--- a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
@@ -19,6 +19,7 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
@@ -35,12 +36,14 @@ public class GnssPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 222;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_GPS_ON, 360.0)
.setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED,
- new double[] {720.0, 1440.0, 1800.0});
+ new double[] {720.0, 1440.0, 1800.0})
+ .initMeasuredEnergyStatsLocked(0);
@Test
public void testTimerBasedModel() {
@@ -51,7 +54,8 @@ public class GnssPowerCalculatorTest {
GnssPowerCalculator calculator =
new GnssPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS))
@@ -59,4 +63,35 @@ public class GnssPowerCalculatorTest {
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(0.1);
}
+
+ @Test
+ public void testMeasuredEnergyBasedModel() {
+ BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID);
+ uidStats.noteStartGps(1000);
+ uidStats.noteStopGps(2000);
+
+ BatteryStatsImpl.Uid uidStats2 = mStatsRule.getUidStats(APP_UID2);
+ uidStats2.noteStartGps(3000);
+ uidStats2.noteStopGps(5000);
+
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ stats.updateGnssMeasuredEnergyStatsLocked(30_000_000, 6000);
+
+ GnssPowerCalculator calculator =
+ new GnssPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS))
+ .isEqualTo(1000);
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isWithin(PRECISION).of(2.77777);
+
+ UidBatteryConsumer consumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+ assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS))
+ .isEqualTo(2000);
+ assertThat(consumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isWithin(PRECISION).of(5.55555);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 66a8379177c2..ae59a546642d 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -24,6 +26,7 @@ import static org.mockito.Mockito.when;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
@@ -54,7 +57,8 @@ public class MobileRadioPowerCalculatorTest {
.setAveragePower(PowerProfile.POWER_RADIO_SCANNING, 720.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX,
- new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0});
+ new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0})
+ .initMeasuredEnergyStatsLocked(0);
@Test
public void testCounterBasedModel() {
@@ -88,14 +92,15 @@ public class MobileRadioPowerCalculatorTest {
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
new int[] {100, 200, 300, 400, 500}, 600);
- stats.noteModemControllerActivity(mai, 10000, 10000);
+ stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
mStatsRule.setTime(12_000_000, 12_000_000);
MobileRadioPowerCalculator calculator =
new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
SystemBatteryConsumer consumer =
mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
@@ -106,4 +111,57 @@ public class MobileRadioPowerCalculatorTest {
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.8);
}
+
+ @Test
+ public void testMeasuredEnergyBasedModel() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+
+ // Note application network activity
+ NetworkStats networkStats = new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100);
+ mStatsRule.setNetworkStats(networkStats);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[] {100, 200, 300, 400, 500}, 600);
+ stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
+
+ mStatsRule.setTime(12_000_000, 12_000_000);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ SystemBatteryConsumer consumer =
+ mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
+
+ // 100000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 2.77777 mAh
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(2.77777);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(1.53934);
+ }
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index e74c936af02d..0df47d6e50bb 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -658,6 +658,11 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState,
elapsedRealtime);
}
+
+ final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
+ if (gnssChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
+ mStats.updateGnssMeasuredEnergyStatsLocked(displayChargeUC, elapsedRealtime);
+ }
}
// Inform mStats about each applicable custom energy bucket.
if (measuredEnergyDeltas != null
@@ -698,7 +703,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
if (modemInfo != null) {
- mStats.noteModemControllerActivity(modemInfo, elapsedRealtime, uptime);
+ final long mobileRadioChargeUC = measuredEnergyDeltas != null
+ ? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+ mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
+ uptime);
}
if (updateFlags == UPDATE_ALL) {
@@ -830,6 +838,12 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
case EnergyConsumerType.CPU_CLUSTER:
buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
break;
+ case EnergyConsumerType.GNSS:
+ buckets[MeasuredEnergyStats.POWER_BUCKET_GNSS] = true;
+ break;
+ case EnergyConsumerType.MOBILE_RADIO:
+ buckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO] = true;
+ break;
case EnergyConsumerType.DISPLAY:
buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true;
buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE] = true;
@@ -883,6 +897,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if ((flags & UPDATE_DISPLAY) != 0) {
addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY);
}
+ if ((flags & UPDATE_RADIO) != 0) {
+ addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.MOBILE_RADIO);
+ }
if ((flags & UPDATE_WIFI) != 0) {
addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.WIFI);
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c3f97adbd9c3..4228e3ef2df9 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1966,7 +1966,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
mHandler.post(() -> {
- mStats.noteModemControllerActivity(info, elapsedRealtime, uptime);
+ mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime,
+ uptime);
});
}
}
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index 4c9ab63a100b..8682027c8eb6 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -118,6 +118,12 @@ public class MeasuredEnergySnapshot {
/** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
public long displayChargeUC = UNAVAILABLE;
+ /** The chargeUC for {@link EnergyConsumerType#GNSS}. */
+ public long gnssChargeUC = UNAVAILABLE;
+
+ /** The chargeUC for {@link EnergyConsumerType#MOBILE_RADIO}. */
+ public long mobileRadioChargeUC = UNAVAILABLE;
+
/** The chargeUC for {@link EnergyConsumerType#WIFI}. */
public long wifiChargeUC = UNAVAILABLE;
@@ -217,6 +223,14 @@ public class MeasuredEnergySnapshot {
output.displayChargeUC = deltaChargeUC;
break;
+ case EnergyConsumerType.GNSS:
+ output.gnssChargeUC = deltaChargeUC;
+ break;
+
+ case EnergyConsumerType.MOBILE_RADIO:
+ output.mobileRadioChargeUC = deltaChargeUC;
+ break;
+
case EnergyConsumerType.WIFI:
output.wifiChargeUC = deltaChargeUC;
break;
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index 73a2febf72aa..4a67ec71fcaa 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -20,6 +20,7 @@ import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO;
import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI;
import static org.junit.Assert.assertArrayEquals;
@@ -93,6 +94,16 @@ public class BatteryExternalStatsWorkerTest {
tempAllIds.add(btId);
mPowerStatsInternal.incrementEnergyConsumption(btId, 34567);
+ final int gnssId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.GNSS, 0,
+ "gnss");
+ tempAllIds.add(gnssId);
+ mPowerStatsInternal.incrementEnergyConsumption(gnssId, 787878);
+
+ final int mobileRadioId = mPowerStatsInternal.addEnergyConsumer(
+ EnergyConsumerType.MOBILE_RADIO, 0, "mobile_radio");
+ tempAllIds.add(mobileRadioId);
+ mPowerStatsInternal.incrementEnergyConsumption(mobileRadioId, 62626);
+
final int[] cpuClusterIds = new int[numCpuClusters];
for (int i = 0; i < numCpuClusters; i++) {
cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer(
@@ -135,6 +146,12 @@ public class BatteryExternalStatsWorkerTest {
assertEquals(1, bluetoothResults.length);
assertEquals(btId, bluetoothResults[0].id);
+ final EnergyConsumerResult[] mobileRadioResults =
+ mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_RADIO).getNow(null);
+ // Results should only have the mobile radio energy consumer
+ assertEquals(1, mobileRadioResults.length);
+ assertEquals(mobileRadioId, mobileRadioResults[0].id);
+
final EnergyConsumerResult[] cpuResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null);
// Results should only have the cpu cluster energy consumers