diff options
10 files changed, 346 insertions, 141 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index ba29a15a91bb..fdc3d650d797 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -43,6 +43,7 @@ public abstract class BatteryConsumer { POWER_COMPONENT_AUDIO, POWER_COMPONENT_VIDEO, POWER_COMPONENT_FLASHLIGHT, + POWER_COMPONENT_MOBILE_RADIO, POWER_COMPONENT_SYSTEM_SERVICES, }) @Retention(RetentionPolicy.SOURCE) @@ -57,8 +58,9 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_VIDEO = 5; public static final int POWER_COMPONENT_FLASHLIGHT = 6; public static final int POWER_COMPONENT_SYSTEM_SERVICES = 7; + public static final int POWER_COMPONENT_MOBILE_RADIO = 8; - public static final int POWER_COMPONENT_COUNT = 8; + public static final int POWER_COMPONENT_COUNT = 9; public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; @@ -87,6 +89,7 @@ public abstract class BatteryConsumer { TIME_COMPONENT_BLUETOOTH, TIME_COMPONENT_CAMERA, TIME_COMPONENT_FLASHLIGHT, + TIME_COMPONENT_MOBILE_RADIO, }) @Retention(RetentionPolicy.SOURCE) public static @interface TimeComponent { @@ -100,8 +103,9 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_AUDIO = 5; public static final int TIME_COMPONENT_VIDEO = 6; public static final int TIME_COMPONENT_FLASHLIGHT = 7; + public static final int TIME_COMPONENT_MOBILE_RADIO = 8; - public static final int TIME_COMPONENT_COUNT = 8; + public static final int TIME_COMPONENT_COUNT = 9; public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999; diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java index 08e358f9094e..49bf08461016 100644 --- a/core/java/android/os/SystemBatteryConsumer.java +++ b/core/java/android/os/SystemBatteryConsumer.java @@ -41,7 +41,7 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable // Reserved: APP DRAIN_TYPE_BLUETOOTH, DRAIN_TYPE_CAMERA, - DRAIN_TYPE_CELL, + DRAIN_TYPE_MOBILE_RADIO, DRAIN_TYPE_FLASHLIGHT, DRAIN_TYPE_IDLE, DRAIN_TYPE_MEMORY, @@ -59,7 +59,7 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable public static final int DRAIN_TYPE_AMBIENT_DISPLAY = 0; public static final int DRAIN_TYPE_BLUETOOTH = 2; public static final int DRAIN_TYPE_CAMERA = 3; - public static final int DRAIN_TYPE_CELL = 4; + public static final int DRAIN_TYPE_MOBILE_RADIO = 4; public static final int DRAIN_TYPE_FLASHLIGHT = 5; public static final int DRAIN_TYPE_IDLE = 6; public static final int DRAIN_TYPE_MEMORY = 7; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index f105320d0353..ee3c12c61771 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -860,14 +860,11 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected int mScreenState = Display.STATE_UNKNOWN; - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected StopwatchTimer mScreenOnTimer; - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected StopwatchTimer mScreenDozeTimer; + StopwatchTimer mScreenOnTimer; + StopwatchTimer mScreenDozeTimer; int mScreenBrightnessBin = -1; - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected final StopwatchTimer[] mScreenBrightnessTimer = + final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; boolean mPretendScreenOff; @@ -912,8 +909,7 @@ public class BatteryStatsImpl extends BatteryStats { int mUsbDataState = USB_DATA_UNKNOWN; int mGpsSignalQualityBin = -1; - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected final StopwatchTimer[] mGpsSignalQualityTimer = + final StopwatchTimer[] mGpsSignalQualityTimer = new StopwatchTimer[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS]; int mPhoneSignalStrengthBin = -1; @@ -929,6 +925,7 @@ public class BatteryStatsImpl extends BatteryStats { final LongSamplingCounter[] mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; + final LongSamplingCounter[] mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; @@ -948,8 +945,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * The Bluetooth controller activity (time in tx, rx, idle, and power consumed) for the device. */ - @VisibleForTesting - protected ControllerActivityCounterImpl mBluetoothActivity; + ControllerActivityCounterImpl mBluetoothActivity; /** * The Modem controller activity (time in tx, rx, idle, and power consumed) for the device. @@ -993,8 +989,7 @@ public class BatteryStatsImpl extends BatteryStats { StopwatchTimer mWifiActiveTimer; int mBluetoothScanNesting; - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected StopwatchTimer mBluetoothScanTimer; + StopwatchTimer mBluetoothScanTimer; int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; long mMobileRadioActiveStartTimeMs; @@ -10718,6 +10713,31 @@ public class BatteryStatsImpl extends BatteryStats { mHandler = new MyHandler(handler.getLooper()); mConstants = new Constants(mHandler); mStartCount++; + initTimersAndCounters(); + mOnBattery = mOnBatteryInternal = false; + long uptimeUs = mClocks.uptimeMillis() * 1000; + long realtimeUs = mClocks.elapsedRealtime() * 1000; + initTimes(uptimeUs, realtimeUs); + mStartPlatformVersion = mEndPlatformVersion = Build.ID; + mDischargeStartLevel = 0; + mDischargeUnplugLevel = 0; + mDischargePlugLevel = -1; + mDischargeCurrentLevel = 0; + mCurrentBatteryLevel = 0; + initDischarge(realtimeUs); + clearHistoryLocked(); + updateDailyDeadlineLocked(); + mPlatformIdleStateCallback = cb; + mMeasuredEnergyRetriever = energyStatsCb; + mUserInfoProvider = userInfoProvider; + + // Notify statsd that the system is initially not in doze. + mDeviceIdleMode = DEVICE_IDLE_MODE_OFF; + FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode); + } + + @VisibleForTesting + protected void initTimersAndCounters() { mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase); mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { @@ -10789,26 +10809,6 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase); mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase); mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase); - mOnBattery = mOnBatteryInternal = false; - long uptimeUs = mClocks.uptimeMillis() * 1000; - long realtimeUs = mClocks.elapsedRealtime() * 1000; - initTimes(uptimeUs, realtimeUs); - mStartPlatformVersion = mEndPlatformVersion = Build.ID; - mDischargeStartLevel = 0; - mDischargeUnplugLevel = 0; - mDischargePlugLevel = -1; - mDischargeCurrentLevel = 0; - mCurrentBatteryLevel = 0; - initDischarge(realtimeUs); - clearHistoryLocked(); - updateDailyDeadlineLocked(); - mPlatformIdleStateCallback = cb; - mMeasuredEnergyRetriever = energyStatsCb; - mUserInfoProvider = userInfoProvider; - - // Notify statsd that the system is initially not in doze. - mDeviceIdleMode = DEVICE_IDLE_MODE_OFF; - FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode); } @UnsupportedAppUsage @@ -11623,7 +11623,8 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("mModemNetworkLock") private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1); - private NetworkStats readNetworkStatsLocked(String[] ifaces) { + @VisibleForTesting + protected NetworkStats readNetworkStatsLocked(String[] ifaces) { try { if (!ArrayUtils.isEmpty(ifaces)) { INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( @@ -11922,7 +11923,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * Distribute Cell radio energy info and network traffic to apps. */ - public void updateMobileRadioState(@Nullable final ModemActivityInfo activityInfo, + public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo, long elapsedRealtimeMs, long uptimeMs) { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating mobile radio stats with " + activityInfo); diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java index 790d2e51e38d..e3bd64d77e9b 100644 --- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java +++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java @@ -15,7 +15,12 @@ */ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.telephony.CellSignalStrength; import android.util.Log; @@ -26,103 +31,146 @@ import java.util.List; public class MobileRadioPowerCalculator extends PowerCalculator { private static final String TAG = "MobRadioPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private final double mPowerRadioOn; - private final double[] mPowerBins = new double[CellSignalStrength.getNumSignalStrengthLevels()]; - private final double mPowerScan; - private BatteryStats mStats; - private long mTotalAppMobileActiveMs = 0; - /** - * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio. - */ - private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) { - final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system - final double MOBILE_POWER = mPowerRadioOn / 3600; + private static final int NUM_SIGNAL_STRENGTH_LEVELS = + CellSignalStrength.getNumSignalStrengthLevels(); - final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, - statsType); - final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, - statsType); - final long mobileData = mobileRx + mobileTx; + private final UsageBasedPowerEstimator mActivePowerEstimator; + private final UsageBasedPowerEstimator[] mIdlePowerEstimators = + new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS]; + private final UsageBasedPowerEstimator mScanPowerEstimator; - final long radioDataUptimeMs = - mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; - final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0) - ? (mobileData / (double) radioDataUptimeMs) - : (((double) MOBILE_BPS) / 8 / 2048); - return (MOBILE_POWER / mobilePps) / (60 * 60); + private static class PowerAndDuration { + public long durationMs; + public double powerMah; + public long totalAppDurationMs; + public long signalDurationMs; + public long noCoverageDurationMs; } public MobileRadioPowerCalculator(PowerProfile profile) { - double temp = + // Power consumption when radio is active + double powerRadioActiveMa = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1); - if (temp != -1) { - mPowerRadioOn = temp; - } else { + if (powerRadioActiveMa == -1) { double sum = 0; sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); - for (int i = 0; i < mPowerBins.length; i++) { + for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); } - mPowerRadioOn = sum / (mPowerBins.length + 1); + powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1); } - temp = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1); - if (temp != -1) { - for (int i = 0; i < mPowerBins.length; i++) { - mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); + mActivePowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa); + + // Power consumption when radio is on, but idle + if (profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1) != -1) { + for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { + mIdlePowerEstimators[i] = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i)); } } else { double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE); - mPowerBins[0] = idle * 25 / 180; - for (int i = 1; i < mPowerBins.length; i++) { - mPowerBins[i] = Math.max(1, idle / 256); + + // Magical calculations preserved for historical compatibility + mIdlePowerEstimators[0] = new UsageBasedPowerEstimator(idle * 25 / 180); + for (int i = 1; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { + mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(Math.max(1, idle / 256)); } } - mPowerScan = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0); + mScanPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0)); + } + + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, + SparseArray<UserHandle> asUsers) { + + PowerAndDuration total = new PowerAndDuration(); + + final double powerPerPacketMah = getMobilePowerPerPacket(batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED); + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + 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); + } + + calculateRemaining(total, batteryStats, rawRealtimeUs); + + if (total.powerMah != 0) { + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO, + total.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, total.powerMah); + } + } + + private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + double powerPerPacketMah, PowerAndDuration total) { + final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED); + total.totalAppDurationMs += radioActiveDurationMs; + + final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs); + + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO, + radioActiveDurationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah); } @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - mStats = batteryStats; - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + final double mobilePowerPerPacket = getMobilePowerPerPacket(batteryStats, rawRealtimeUs, + statsType); + PowerAndDuration total = new PowerAndDuration(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + final BatteryStats.Uid u = app.uidObj; + calculateApp(app, u, statsType, mobilePowerPerPacket, total); + } + } BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); - calculateRemaining(radio, mStats, rawRealtimeUs, rawUptimeUs, statsType); - radio.sumPower(); + calculateRemaining(total, batteryStats, rawRealtimeUs); + if (total.powerMah != 0) { + if (total.signalDurationMs != 0) { + radio.noCoveragePercent = + total.noCoverageDurationMs * 100.0 / total.signalDurationMs; + } + radio.mobileActive = total.durationMs; + radio.mobileActiveCount = batteryStats.getMobileRadioActiveUnknownCount(statsType); + radio.mobileRadioPowerMah = total.powerMah; + radio.sumPower(); + } if (radio.totalPowerMah > 0) { sippers.add(radio); } } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, + double powerPerPacketMah, PowerAndDuration total) { + app.mobileActive = calculateDuration(u, statsType); + app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive); + total.totalAppDurationMs += app.mobileActive; + // Add cost of mobile traffic. app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, statsType); app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, statsType); - app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000; app.mobileActiveCount = u.getMobileRadioActiveCount(statsType); app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA, statsType); app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA, statsType); - if (app.mobileActive > 0) { - // We are tracking when the radio is up, so can use the active time to - // determine power use. - mTotalAppMobileActiveMs += app.mobileActive; - app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000 * 60 * 60); - } else { - // We are not tracking when the radio is up, so must approximate power use - // based on the number of packets. - app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) - * getMobilePowerPerPacket(rawRealtimeUs, statsType); - } if (DEBUG && app.mobileRadioPowerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": mobile packets " + (app.mobileRxPackets + app.mobileTxPackets) @@ -131,52 +179,80 @@ public class MobileRadioPowerCalculator extends PowerCalculator { } } - private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - double power = 0; + private long calculateDuration(BatteryStats.Uid u, int statsType) { + return u.getMobileRadioActiveTime(statsType) / 1000; + } + + private double calculatePower(BatteryStats.Uid u, double powerPerPacketMah, + long radioActiveDurationMs) { + 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); + } else { + // We are not tracking when the radio is up, so must approximate power use + // based on the number of packets. + final long mobileRxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_MOBILE_RX_DATA, + BatteryStats.STATS_SINCE_CHARGED); + final long mobileTxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_MOBILE_TX_DATA, + BatteryStats.STATS_SINCE_CHARGED); + return (mobileRxPackets + mobileTxPackets) * powerPerPacketMah; + } + } + + private void calculateRemaining(MobileRadioPowerCalculator.PowerAndDuration total, + BatteryStats batteryStats, long rawRealtimeUs) { long signalTimeMs = 0; - long noCoverageTimeMs = 0; - for (int i = 0; i < mPowerBins.length; i++) { - long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType) - / 1000; - final double p = (strengthTimeMs * mPowerBins[i]) / (60 * 60 * 1000); + double powerMah = 0; + 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)); } - power += p; + powerMah += p; signalTimeMs += strengthTimeMs; if (i == 0) { - noCoverageTimeMs = strengthTimeMs; + total.noCoverageDurationMs = strengthTimeMs; } } - final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType) - / 1000; - final double p = (scanningTimeMs * mPowerScan) / (60 * 60 * 1000); + 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)); } - power += p; - long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; - long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs; + powerMah += p; + long radioActiveTimeMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs; if (remainingActiveTimeMs > 0) { - power += (mPowerRadioOn * remainingActiveTimeMs) / (1000 * 60 * 60); - } - - if (power != 0) { - if (signalTimeMs != 0) { - app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs; - } - app.mobileActive = remainingActiveTimeMs; - app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType); - app.mobileRadioPowerMah = power; + powerMah += mActivePowerEstimator.calculatePower(remainingActiveTimeMs); } + total.durationMs = radioActiveTimeMs; + total.powerMah = powerMah; + total.signalDurationMs = signalTimeMs; } - @Override - public void reset() { - mTotalAppMobileActiveMs = 0; - mStats = null; + /** + * 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 long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, + statsType); + final long mobileTx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, + statsType); + final long mobilePackets = mobileRx + mobileTx; + + return mobilePackets != 0 ? mobilePower / mobilePackets : 0; } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 8ff318e8d555..dc332533b2ca 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -58,6 +58,7 @@ import org.junit.runners.Suite; KernelWakelockReaderTest.class, LongSamplingCounterTest.class, LongSamplingCounterArrayTest.class, + MobileRadioPowerCalculatorTest.class, PowerCalculatorTest.class, PowerProfileTest.class, ScreenPowerCalculatorTest.class, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 55f64f977933..59534e4a84ef 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -16,10 +16,13 @@ package com.android.internal.os; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; +import android.net.NetworkStats; import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; @@ -32,6 +35,7 @@ import androidx.test.InstrumentationRegistry; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import org.mockito.stubbing.Answer; public class BatteryUsageStatsRule implements TestRule { private final PowerProfile mPowerProfile; @@ -53,6 +57,14 @@ public class BatteryUsageStatsRule implements TestRule { public BatteryUsageStatsRule setAveragePower(String key, double value) { when(mPowerProfile.getAveragePower(key)).thenReturn(value); + when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble())).thenReturn(value); + return this; + } + + public BatteryUsageStatsRule setAveragePowerUnspecified(String key) { + when(mPowerProfile.getAveragePower(key)).thenReturn(0.0); + when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble())) + .thenAnswer((Answer<Double>) invocation -> (Double) invocation.getArguments()[1]); return this; } @@ -64,6 +76,10 @@ public class BatteryUsageStatsRule implements TestRule { return this; } + public void setNetworkStats(NetworkStats networkStats) { + mBatteryStats.setNetworkStats(networkStats); + } + @Override public Statement apply(Statement base, Description description) { return new Statement() { @@ -76,6 +92,7 @@ public class BatteryUsageStatsRule implements TestRule { } private void noteOnBattery() { + mBatteryStats.setOnBatteryInternal(true); mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); } diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java new file mode 100644 index 000000000000..4230066f32a1 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.net.ConnectivityManager; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; +import android.telephony.DataConnectionRealTimeInfo; +import android.telephony.ModemActivityInfo; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MobileRadioPowerCalculatorTest { + private static final double PRECISION = 0.00001; + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePowerUnspecified(PowerProfile.POWER_RADIO_ACTIVE) + .setAveragePowerUnspecified(PowerProfile.POWER_RADIO_ON) + .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE, 360.0) + .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}); + + @Test + public void testCounterBasedModel() { + 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.noteNetworkInterfaceType("cellular", ConnectivityManager.TYPE_MOBILE); + + // 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, 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); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(1.44440); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) + .isWithin(PRECISION).of(0.8); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index fc237219be51..bf74c8d82fa2 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -16,7 +16,7 @@ package com.android.internal.os; -import android.location.GnssSignalQuality; +import android.net.NetworkStats; import android.os.Handler; import android.os.Looper; import android.util.SparseIntArray; @@ -38,26 +38,14 @@ import java.util.concurrent.Future; public class MockBatteryStatsImpl extends BatteryStatsImpl { public BatteryStatsImpl.Clocks clocks; public boolean mForceOnBattery; + private NetworkStats mNetworkStats; MockBatteryStatsImpl(Clocks clocks) { super(clocks); this.clocks = mClocks; - mScreenOnTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null, - mOnBatteryTimeBase); - mScreenDozeTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null, - mOnBatteryTimeBase); - for (int i = 0; i < mScreenBrightnessTimer.length; i++) { - mScreenBrightnessTimer[i] = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null, - mOnBatteryTimeBase); - } - mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase); - mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, 1); - setExternalStatsSyncLocked(new DummyExternalStatsSync()); + initTimersAndCounters(); - for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) { - mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000 - i, null, - mOnBatteryTimeBase); - } + setExternalStatsSyncLocked(new DummyExternalStatsSync()); final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; Arrays.fill(supportedBuckets, true); @@ -106,6 +94,16 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return getUidStatsLocked(uid).mOnBatteryScreenOffBackgroundTimeBase; } + public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) { + mNetworkStats = networkStats; + return this; + } + + @Override + protected NetworkStats readNetworkStatsLocked(String[] ifaces) { + return mNetworkStats; + } + public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) { mPowerProfile = powerProfile; return this; diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index ada7eeab9da3..a3fac0510f20 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -593,7 +593,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } if (modemInfo != null) { - mStats.updateMobileRadioState(modemInfo, elapsedRealtime, uptime); + mStats.noteModemControllerActivity(modemInfo, elapsedRealtime, uptime); } if (updateFlags == UPDATE_ALL) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index cc23e233aab0..b1cbb4acdb13 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1853,7 +1853,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { - mStats.updateMobileRadioState(info, elapsedRealtime, uptime); + mStats.noteModemControllerActivity(info, elapsedRealtime, uptime); }); } } |