summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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