diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index d622fd717611d..f9482dd2b0287 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -310,6 +310,24 @@ public class BatteryStatsImpl extends BatteryStats { /** Container for Rail Energy Data stats. */ private final RailStats mTmpRailStats = new RailStats(); + /** + * Estimate UID modem power usage based on their estimated mobile radio active time. + */ + public static final int PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME = 1; + /** + * Estimate UID modem power consumption by proportionally attributing estimated Rx and Tx + * power consumption individually. + * ModemActivityInfo must be available. + */ + public static final int PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX = 2; + @IntDef(flag = true, prefix = "PER_UID_MODEM_MODEL_", value = { + PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME, + PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PerUidModemPowerModel { + } + /** * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader}, * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader}, @@ -12038,8 +12056,7 @@ public class BatteryStatsImpl extends BatteryStats { } final SparseDoubleArray uidEstimatedConsumptionMah; - if (consumedChargeUC > 0 && mMobileRadioPowerCalculator != null - && mGlobalEnergyConsumerStats != null) { + if (consumedChargeUC > 0 && isMobileRadioEnergyConsumerSupportedLocked()) { mGlobalEnergyConsumerStats.updateStandardBucket( EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC); uidEstimatedConsumptionMah = new SparseDoubleArray(); @@ -12047,6 +12064,8 @@ public class BatteryStatsImpl extends BatteryStats { uidEstimatedConsumptionMah = null; } + RxTxConsumption rxTxConsumption = null; + boolean attributeWithModemActivityInfo = false; if (deltaInfo != null) { mHasModemReporting = true; mModemActivity.getOrCreateIdleTimeCounter() @@ -12092,7 +12111,11 @@ public class BatteryStatsImpl extends BatteryStats { mTmpRailStats.resetCellularTotalEnergyUsed(); } - incrementPerRatDataLocked(deltaInfo, elapsedRealtimeMs); + rxTxConsumption = incrementPerRatDataLocked(deltaInfo, elapsedRealtimeMs); + + attributeWithModemActivityInfo = mConstants.PER_UID_MODEM_MODEL + == PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX + && rxTxConsumption != null; } long totalAppRadioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000); @@ -12156,12 +12179,23 @@ public class BatteryStatsImpl extends BatteryStats { (totalAppRadioTimeUs * appPackets) / totalPackets; u.noteMobileRadioActiveTimeLocked(appRadioTimeUs, elapsedRealtimeMs); - // Distribute mobile radio charge consumption based on app radio - // active time if (uidEstimatedConsumptionMah != null) { - uidEstimatedConsumptionMah.incrementValue(u.getUid(), + final double uidConsumptionMah; + if (attributeWithModemActivityInfo) { + // Distribute measured mobile radio charge consumption based on + // rx/tx packets and estimated rx/tx charge consumption. + uidConsumptionMah = smearModemActivityInfoRxTxConsumptionMah( + rxTxConsumption, entry.getRxPackets(), entry.getTxPackets(), + totalRxPackets, totalTxPackets); + } else { + // Distribute mobile radio charge consumption based on app radio + // active time + uidConsumptionMah = mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah( - appRadioTimeUs / 1000)); + appRadioTimeUs / 1000); + } + uidEstimatedConsumptionMah.incrementValue(u.getUid(), + uidConsumptionMah); } // Remove this app from the totals, so that we don't lose any time @@ -12199,54 +12233,95 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveUnknownCount.addCountLocked(1); } - // Update the EnergyConsumerStats 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( + if (attributeWithModemActivityInfo) { + // Estimate inactive modem power consumption and combine with previously + // estimated active power consumption for an estimate of total modem + // power consumption. + final long sleepTimeMs = deltaInfo.getSleepTimeMillis(); + final long idleTimeMs = deltaInfo.getIdleTimeMillis(); + final double inactiveConsumptionMah = + mMobileRadioPowerCalculator.calcInactiveStatePowerMah(sleepTimeMs, + idleTimeMs); + totalEstimatedConsumptionMah += inactiveConsumptionMah; + totalEstimatedConsumptionMah += rxTxConsumption.rxConsumptionMah; + totalEstimatedConsumptionMah += rxTxConsumption.txConsumptionMah; + } else { + // Estimate total active radio power consumption since last mark. + final long totalRadioTimeMs = + mMobileRadioActiveTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; - mPhoneSignalStrengthsTimer[strengthLevel].setMark(elapsedRealtimeMs); - + mMobileRadioActiveTimer.setMark(elapsedRealtimeMs); totalEstimatedConsumptionMah += - mMobileRadioPowerCalculator.calcIdlePowerAtSignalStrengthMah( - strengthLevelDurationMs, strengthLevel); + mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah( + totalRadioTimeMs); + + // Estimate idle power consumption at each signal strength level + final int numSignalStrengthLevels = mPhoneSignalStrengthsTimer.length; + for (int lvl = 0; lvl < numSignalStrengthLevels; lvl++) { + final long strengthLevelDurationMs = + mPhoneSignalStrengthsTimer[lvl].getTimeSinceMarkLocked( + elapsedRealtimeMs * 1000) / 1000; + mPhoneSignalStrengthsTimer[lvl].setMark(elapsedRealtimeMs); + + totalEstimatedConsumptionMah += + mMobileRadioPowerCalculator.calcIdlePowerAtSignalStrengthMah( + strengthLevelDurationMs, lvl); + } + + // Estimate total active radio power consumption since last mark. + final long scanTimeMs = mPhoneSignalScanningTimer.getTimeSinceMarkLocked( + elapsedRealtimeMs * 1000) / 1000; + mPhoneSignalScanningTimer.setMark(elapsedRealtimeMs); + totalEstimatedConsumptionMah += + mMobileRadioPowerCalculator.calcScanTimePowerMah(scanTimeMs); } - - // 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(EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah, elapsedRealtimeMs); } + } + } + } - delta = null; + private static class RxTxConsumption { + public final double rxConsumptionMah; + public final long rxDurationMs; + public final double txConsumptionMah; + public final long txDurationMs; + + /** + * Represents the ratio between time spent transmitting and the total active time. + */ + public final double txToTotalRatio; + + RxTxConsumption(double rxMah, long rxMs, double txMah, long txMs) { + rxConsumptionMah = rxMah; + rxDurationMs = rxMs; + txConsumptionMah = txMah; + txDurationMs = txMs; + + final long activeDurationMs = txDurationMs + rxDurationMs; + if (activeDurationMs == 0) { + txToTotalRatio = 0.0; + } else { + txToTotalRatio = ((double) txDurationMs) / activeDurationMs; } } } @GuardedBy("this") - private void incrementPerRatDataLocked(ModemActivityInfo deltaInfo, long elapsedRealtimeMs) { - final int infoSize = deltaInfo.getSpecificInfoLength(); + @Nullable + private RxTxConsumption incrementPerRatDataLocked(ModemActivityInfo deltaInfo, + long elapsedRealtimeMs) { + double rxConsumptionMah = 0.0; + long rxDurationMs = 0; + double txConsumptionMah = 0.0; + long txDurationMs = 0; + final int infoSize = deltaInfo.getSpecificInfoLength(); if (infoSize == 1 && deltaInfo.getSpecificInfoRat(0) == AccessNetworkConstants.AccessNetworkType.UNKNOWN && deltaInfo.getSpecificInfoFrequencyRange(0) @@ -12296,6 +12371,16 @@ public class BatteryStatsImpl extends BatteryStats { + (totalLvlDurationMs / 2)) / totalLvlDurationMs; ratStats.incrementTxDuration(freq, level, proportionalTxDurationMs); frequencyDurationMs += durationMs; + + if (isMobileRadioEnergyConsumerSupportedLocked()) { + // Accumulate the power cost of time spent transmitting in a + // particular state. + final double txStatePowerConsumptionMah = + mMobileRadioPowerCalculator.calcTxStatePowerMah(rat, freq, + level, proportionalTxDurationMs); + txConsumptionMah += txStatePowerConsumptionMah; + txDurationMs += proportionalTxDurationMs; + } } final long totalRxDuration = deltaInfo.getReceiveTimeMillis(); // Smear HAL provided Rx power duration based on active modem @@ -12305,6 +12390,16 @@ public class BatteryStatsImpl extends BatteryStats { (frequencyDurationMs * totalRxDuration + (totalActiveTimeMs / 2)) / totalActiveTimeMs; ratStats.incrementRxDuration(freq, proportionalRxDurationMs); + + if (isMobileRadioEnergyConsumerSupportedLocked()) { + // Accumulate the power cost of time spent receiving in a particular + // state. + final double rxStatePowerConsumptionMah = + mMobileRadioPowerCalculator.calcRxStatePowerMah(rat, freq, + proportionalRxDurationMs); + rxConsumptionMah += rxStatePowerConsumptionMah; + rxDurationMs += proportionalRxDurationMs; + } } } @@ -12324,9 +12419,28 @@ public class BatteryStatsImpl extends BatteryStats { final int[] txTimesMs = deltaInfo.getTransmitTimeMillis(rat, freq); ratStats.incrementRxDuration(freq, rxTimeMs); + if (isMobileRadioEnergyConsumerSupportedLocked()) { + // Accumulate the power cost of time spent receiving in a particular state. + final double rxStatePowerConsumptionMah = + mMobileRadioPowerCalculator.calcRxStatePowerMah(ratBucket, freq, + rxTimeMs); + rxConsumptionMah += rxStatePowerConsumptionMah; + rxDurationMs += rxTimeMs; + } + final int numTxLvl = txTimesMs.length; for (int lvl = 0; lvl < numTxLvl; lvl++) { - ratStats.incrementTxDuration(freq, lvl, txTimesMs[lvl]); + final long txTimeMs = txTimesMs[lvl]; + ratStats.incrementTxDuration(freq, lvl, txTimeMs); + if (isMobileRadioEnergyConsumerSupportedLocked()) { + // Accumulate the power cost of time spent transmitting in a particular + // state. + final double txStatePowerConsumptionMah = + mMobileRadioPowerCalculator.calcTxStatePowerMah(ratBucket, freq, + lvl, txTimeMs); + txConsumptionMah += txStatePowerConsumptionMah; + txDurationMs += txTimeMs; + } } } } @@ -12336,6 +12450,45 @@ public class BatteryStatsImpl extends BatteryStats { if (ratStats == null) continue; ratStats.setMark(elapsedRealtimeMs); } + + if (isMobileRadioEnergyConsumerSupportedLocked()) { + return new RxTxConsumption(rxConsumptionMah, rxDurationMs, txConsumptionMah, + txDurationMs); + } else { + return null; + } + } + + /** + * Smear modem Rx/Tx power consumption calculated from {@link ModemActivityInfo} using Rx/Tx + * packets. + * + * @return the combine Rx/Tx smeared power consumption in milliamp-hours. + */ + private double smearModemActivityInfoRxTxConsumptionMah(RxTxConsumption rxTxConsumption, + long rxPackets, long txPackets, long totalRxPackets, long totalTxPackets) { + // Distribute measured mobile radio charge consumption based on + // rx/tx packets and estimated rx/tx charge consumption. + double consumptionMah = 0.0; + if (totalRxPackets != 0) { + // Proportionally distribute receive battery consumption. + consumptionMah += rxTxConsumption.rxConsumptionMah * rxPackets + / totalRxPackets; + } + if (totalTxPackets != 0 || (totalRxPackets != 0 && rxTxConsumption.txToTotalRatio != 0.0)) { + // ModemActivityInfo Tx time represents time spent both transmitting and receiving. + // There is currently no way to distinguish how many Rx packets were received during + // Rx time vs Tx time. + // Assumption: The number of packets received while transmitting is proportional + // to the time spent transmitting over total active time. + final double totalPacketsDuringTxTime = + totalTxPackets + rxTxConsumption.txToTotalRatio * totalRxPackets; + final double packetsDuringTxTime = + txPackets + rxTxConsumption.txToTotalRatio * rxPackets; + consumptionMah += rxTxConsumption.txConsumptionMah * packetsDuringTxTime + / totalPacketsDuringTxTime; + } + return consumptionMah; } /** @@ -14844,6 +14997,13 @@ public class BatteryStatsImpl extends BatteryStats { } } + @GuardedBy("this") + private boolean isMobileRadioEnergyConsumerSupportedLocked() { + if (mGlobalEnergyConsumerStats == null) return false; + return mGlobalEnergyConsumerStats.isStandardBucketSupported( + EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO); + } + @NonNull private static String[] getBatteryConsumerProcessStateNames() { String[] procStateNames = new String[BatteryConsumer.PROCESS_STATE_COUNT]; @@ -14879,6 +15039,40 @@ public class BatteryStatsImpl extends BatteryStats { "battery_charged_delay_ms"; public static final String KEY_RECORD_USAGE_HISTORY = "record_usage_history"; + public static final String KEY_PER_UID_MODEM_POWER_MODEL = + "per_uid_modem_power_model"; + + public static final String PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME_NAME = + "mobile_radio_active_time"; + public static final String PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX_NAME = + "modem_activity_info_rx_tx"; + + /** Convert {@link PerUidModemPowerModel} to string */ + public String getPerUidModemModelName(@PerUidModemPowerModel int model) { + switch(model) { + case PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME: + return PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME_NAME; + case PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX: + return PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX_NAME; + default: + Slog.w(TAG, "Unexpected per uid modem model (" + model + ")"); + return "unknown_" + model; + } + } + + /** Convert string to {@link PerUidModemPowerModel} */ + @PerUidModemPowerModel + public int getPerUidModemModel(String name) { + switch(name) { + case PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME_NAME: + return PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME; + case PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX_NAME: + return PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX; + default: + Slog.w(TAG, "Unexpected per uid modem model name (" + name + ")"); + return DEFAULT_PER_UID_MODEM_MODEL; + } + } private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000; @@ -14892,6 +15086,9 @@ public class BatteryStatsImpl extends BatteryStats { private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/ private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */ private static final boolean DEFAULT_RECORD_USAGE_HISTORY = false; + @PerUidModemPowerModel + private static final int DEFAULT_PER_UID_MODEM_MODEL = + PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX; public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; /* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an @@ -14908,6 +15105,7 @@ public class BatteryStatsImpl extends BatteryStats { public int MAX_HISTORY_BUFFER; /*Bytes*/ public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS; public boolean RECORD_USAGE_HISTORY = DEFAULT_RECORD_USAGE_HISTORY; + public int PER_UID_MODEM_MODEL = DEFAULT_PER_UID_MODEM_MODEL; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -14985,6 +15183,9 @@ public class BatteryStatsImpl extends BatteryStats { * 1024; RECORD_USAGE_HISTORY = mParser.getBoolean( KEY_RECORD_USAGE_HISTORY, DEFAULT_RECORD_USAGE_HISTORY); + final String perUidModemModel = mParser.getString(KEY_PER_UID_MODEM_POWER_MODEL, + ""); + PER_UID_MODEM_MODEL = getPerUidModemModel(perUidModemModel); updateBatteryChargedDelayMsLocked(); @@ -15053,6 +15254,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(BATTERY_CHARGED_DELAY_MS); pw.print(KEY_RECORD_USAGE_HISTORY); pw.print("="); pw.println(RECORD_USAGE_HISTORY); + pw.print(KEY_PER_UID_MODEM_POWER_MODEL); pw.print("="); + pw.println(getPerUidModemModelName(PER_UID_MODEM_MODEL)); } } diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java index 4608e9a213b12..3226260a2c5dc 100644 --- a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java @@ -270,25 +270,28 @@ public class MobileRadioPowerCalculator extends PowerCalculator { // Calculate the inactive modem power consumption. final BatteryStats.ControllerActivityCounter modemActivity = batteryStats.getModemControllerActivity(); - if (modemActivity != null && (mSleepPowerEstimator != null - || mIdlePowerEstimator != null)) { + double inactivePowerMah = Double.NaN; + if (modemActivity != null) { final long sleepDurationMs = modemActivity.getSleepTimeCounter().getCountLocked( BatteryStats.STATS_SINCE_CHARGED); - total.remainingPowerMah += mSleepPowerEstimator.calculatePower(sleepDurationMs); final long idleDurationMs = modemActivity.getIdleTimeCounter().getCountLocked( BatteryStats.STATS_SINCE_CHARGED); - total.remainingPowerMah += mIdlePowerEstimator.calculatePower(idleDurationMs); - } else { + inactivePowerMah = calcInactiveStatePowerMah(sleepDurationMs, idleDurationMs); + } + if (Double.isNaN(inactivePowerMah)) { // Modem activity counters unavailable. Use legacy calculations for inactive usage. final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; - total.remainingPowerMah += calcScanTimePowerMah(scanningTimeMs); + inactivePowerMah = calcScanTimePowerMah(scanningTimeMs); for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; - total.remainingPowerMah += calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i); + inactivePowerMah += calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i); } } + if (!Double.isNaN(inactivePowerMah)) { + total.remainingPowerMah += inactivePowerMah; + } } @@ -508,6 +511,22 @@ public class MobileRadioPowerCalculator extends PowerCalculator { return consumptionMah; } + /** + * Calculates active transmit radio power consumption (in milliamp-hours) from the given state's + * duration. + */ + public double calcInactiveStatePowerMah(long sleepDurationMs, long idleDurationMs) { + if (mSleepPowerEstimator == null || mIdlePowerEstimator == null) return Double.NaN; + final double sleepConsumptionMah = mSleepPowerEstimator.calculatePower(sleepDurationMs); + final double idleConsumptionMah = mIdlePowerEstimator.calculatePower(idleDurationMs); + if (DEBUG) { + Log.d(TAG, "Calculated sleep consumption " + sleepConsumptionMah + + " mAH from a duration of " + sleepDurationMs + " ms and idle consumption " + + idleConsumptionMah + " mAH from a duration of " + idleDurationMs); + } + return sleepConsumptionMah + idleConsumptionMah; + } + /** * Calculates active radio power consumption (in milliamp-hours) from active radio duration. */ diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index bf2faac210824..3135215d65f7a 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java @@ -137,6 +137,13 @@ public class BatteryUsageStatsRule implements TestRule { return this; } + public BatteryUsageStatsRule setPerUidModemModel(int perUidModemModel) { + synchronized (mBatteryStats) { + mBatteryStats.setPerUidModemModel(perUidModemModel); + } + return this; + } + /** Call only after setting the power profile information. */ public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() { return initMeasuredEnergyStatsLocked(new String[0]); diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java index 65e648671f601..d059472d677a9 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java @@ -574,8 +574,10 @@ public class MobileRadioPowerCalculatorTest { } @Test - public void testMeasuredEnergyBasedModel() { + public void testMeasuredEnergyBasedModel_mobileRadioActiveTimeModel() { mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) + .setPerUidModemModel( + BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME) .initMeasuredEnergyStatsLocked(); BatteryStatsImpl stats = mStatsRule.getBatteryStats(); @@ -637,6 +639,263 @@ public class MobileRadioPowerCalculatorTest { .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } + + + @Test + public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel() { + mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator_multiactive) + .setPerUidModemModel( + BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX) + .initMeasuredEnergyStatsLocked(); + 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); + + ArrayList perRatCellStrength = new ArrayList(); + CellSignalStrength gsmSignalStrength = mock(CellSignalStrength.class); + when(gsmSignalStrength.getLevel()).thenReturn( + SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + perRatCellStrength.add(gsmSignalStrength); + + // Note cell signal strength + SignalStrength signalStrength = mock(SignalStrength.class); + when(signalStrength.getCellSignalStrengths()).thenReturn(perRatCellStrength); + 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}); + + // Spend some time in each signal strength level. It doesn't matter how long. + // The ModemActivityInfo reported level residency should be trusted over the BatteryStats + // timers. + when(gsmSignalStrength.getLevel()).thenReturn( + SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + stats.notePhoneSignalStrengthLocked(signalStrength, 8111, 8111); + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_POOR); + stats.notePhoneSignalStrengthLocked(signalStrength, 8333, 8333); + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE); + stats.notePhoneSignalStrengthLocked(signalStrength, 8666, 8666); + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_GOOD); + stats.notePhoneSignalStrengthLocked(signalStrength, 9110, 9110); + + stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, + 9_500_000_000L, APP_UID2, 9500, 9500); + + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_GREAT); + stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); + + // Note application network activity + NetworkStats networkStats = new NetworkStats(10000, 1) + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 300, 10, 100)) + .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 2000, 30, 111)); + mStatsRule.setNetworkStats(networkStats); + + ActivityStatsTechSpecificInfo cdmaInfo = new ActivityStatsTechSpecificInfo( + AccessNetwork.CDMA2000, ServiceState.FREQUENCY_RANGE_UNKNOWN, + new int[]{10, 11, 12, 13, 14}, 15); + ActivityStatsTechSpecificInfo lteInfo = new ActivityStatsTechSpecificInfo( + AccessNetwork.EUTRAN, ServiceState.FREQUENCY_RANGE_UNKNOWN, + new int[]{20, 21, 22, 23, 24}, 25); + ActivityStatsTechSpecificInfo nrLowFreqInfo = new ActivityStatsTechSpecificInfo( + AccessNetwork.NGRAN, ServiceState.FREQUENCY_RANGE_LOW, + new int[]{30, 31, 32, 33, 34}, 35); + ActivityStatsTechSpecificInfo nrMidFreqInfo = new ActivityStatsTechSpecificInfo( + AccessNetwork.NGRAN, ServiceState.FREQUENCY_RANGE_MID, + new int[]{40, 41, 42, 43, 44}, 45); + ActivityStatsTechSpecificInfo nrHighFreqInfo = new ActivityStatsTechSpecificInfo( + AccessNetwork.NGRAN, ServiceState.FREQUENCY_RANGE_HIGH, + new int[]{50, 51, 52, 53, 54}, 55); + ActivityStatsTechSpecificInfo nrMmwaveFreqInfo = new ActivityStatsTechSpecificInfo( + AccessNetwork.NGRAN, ServiceState.FREQUENCY_RANGE_MMWAVE, + new int[]{60, 61, 62, 63, 64}, 65); + + ActivityStatsTechSpecificInfo[] ratInfos = + new ActivityStatsTechSpecificInfo[]{cdmaInfo, lteInfo, nrLowFreqInfo, nrMidFreqInfo, + nrHighFreqInfo, nrMmwaveFreqInfo}; + + ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, ratInfos); + stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000, + mNetworkStatsManager); + + mStatsRule.setTime(10_000, 10_000); + + MobileRadioPowerCalculator calculator = + new MobileRadioPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); + assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(2.77778); + assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + + // CDMA2000 [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration + // [720, 1080, 1440, 1800, 2160, 1440] mA . [10, 11, 12, 13, 14, 15] ms = 111600 mA-ms + // LTE [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration + // [800, 1200, 1600, 2000, 2400, 2000] mA . [20, 21, 22, 23, 24, 25] ms = 230000 mA-ms + // 5G Low Frequency [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration + // (nrFrequency="LOW" values was not defined so fall back to nrFrequency="DEFAULT") + // [999, 1333, 1888, 2222, 2666, 2222] mA . [30, 31, 32, 33, 34, 35] ms = 373449 mA-ms + // 5G Mid Frequency [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration + // (nrFrequency="MID" values was not defined so fall back to nrFrequency="DEFAULT") + // [999, 1333, 1888, 2222, 2666, 2222] mA . [40, 41, 42, 43, 44, 45] ms = 486749 mA-ms + // 5G High Frequency [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration + // [1818, 2727, 3636, 4545, 5454, 2727] mA . [50, 51, 52, 53, 54, 55] ms = 1104435 mA-ms + // 5G Mmwave Frequency [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration + // [2345, 3456, 4567, 5678, 6789, 3456] mA . [60, 61, 62, 63, 64, 65] ms = 1651520 mA-ms + // _________________ + // = 3957753 mA-ms estimated active consumption + // + // Idle drain rate * idle duration + // 360 mA * 3000 ms = 1080000 mA-ms + // Sleep drain rate * sleep duration + // 70 mA * 2000 ms = 140000 mA-ms + // _________________ + // = 5177753 mA-ms estimated total consumption + // + // 2.77778 mA-h measured total consumption * 3957753 / 5177753 = 2.123268 mA-h + BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); + assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(2.123268); + assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + + // 240 ms Rx Time, 1110 ms Tx Time, 1350 ms active time + // 150 App 1 Rx Packets, 10 App 1 Tx packets + // 50 App 2 Rx Packets, 30 App 2 Tx packets + // 200 total Rx Packets, 40 total Tx packets + // 623985 mA-ms Rx consumption, 3333768 mA-ms Tx consumption + // + // Rx Power consumption * Ratio of App1 / Total Rx Packets: + // 623985 * 150 / 200 = 467988.75 mA-ms App 1 Rx Power Consumption + // + // App 1 Tx Packets + App 1 Rx Packets * Ratio of Tx / Total active time + // 10 + 150 * 1110 / 1350 = 133.3333 Estimated App 1 Rx/Tx Packets during Tx + // Total Tx Packets + Total Rx Packets * Ratio of Tx / Total active time + // 40 + 200 * 1110 / 1350 = 204.44444 Estimated Total Rx/Tx Packets during Tx + // Tx Power consumption * Ratio of App 1 / Total Estimated Tx Packets: + // 3333768 * 133.33333 / 204.44444 = 2174196.52174 mA-ms App 1 Tx Power Consumption + // + // Total App Power consumption * Ratio of App 1 / Total Estimated Power Consumption + // 2.123268 * (467988.75 + 2174196.52174) / 3957753 = 1.41749 App 1 Power Consumption + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(1.41749); + assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + + // Rest should go to the other app + UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); + assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(0.705778); + assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + } + + @Test + public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel_legacyPowerProfile() { + mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) + .setPerUidModemModel( + BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX) + .initMeasuredEnergyStatsLocked(); + BatteryStatsImpl stats = mStatsRule.getBatteryStats(); + + // Scan for a cell + stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE, + TelephonyManager.SIM_STATE_READY, + 2000, 2000); + + ArrayList perRatCellStrength = new ArrayList(); + CellSignalStrength gsmSignalStrength = mock(CellSignalStrength.class); + when(gsmSignalStrength.getLevel()).thenReturn( + SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + perRatCellStrength.add(gsmSignalStrength); + + // 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.getCellSignalStrengths()).thenReturn(perRatCellStrength); + 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}); + + // Spend some time in each signal strength level. It doesn't matter how long. + // The ModemActivityInfo reported level residency should be trusted over the BatteryStats + // timers. + when(gsmSignalStrength.getLevel()).thenReturn( + SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + stats.notePhoneSignalStrengthLocked(signalStrength, 8111, 8111); + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_POOR); + stats.notePhoneSignalStrengthLocked(signalStrength, 8333, 8333); + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE); + stats.notePhoneSignalStrengthLocked(signalStrength, 8666, 8666); + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_GOOD); + stats.notePhoneSignalStrengthLocked(signalStrength, 9110, 9110); + when(gsmSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_GREAT); + stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); + + // Note application network activity + NetworkStats networkStats = new NetworkStats(10000, 1) + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 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, mNetworkStatsManager); + + mStatsRule.setTime(12_000, 12_000); + + MobileRadioPowerCalculator calculator = + new MobileRadioPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); + // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh + assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(2.77778); + assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + + BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); + // Estimated Rx/Tx modem consumption = 0.94 mAh + // Estimated total modem consumption = 1.27888 mAh + // 2.77778 * 0.94 / 1.27888 = 2.04170 mAh + assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(2.04170); + assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(2.04170); + assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + } + @Test public void testMeasuredEnergyBasedModel_byProcessState() { mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java index 19d2639311a38..f8033557f0f0c 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java @@ -213,6 +213,13 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return this; } + @GuardedBy("this") + public MockBatteryStatsImpl setPerUidModemModel(int perUidModemModel) { + mConstants.PER_UID_MODEM_MODEL = perUidModemModel; + mConstants.onChange(); + return this; + } + public int getAndClearExternalStatsSyncFlags() { final int flags = mExternalStatsSync.flags; mExternalStatsSync.flags = 0;