diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 5f9fdbfe7503a..08f1fdd07760a 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2700,6 +2700,46 @@ public abstract class BatteryStats implements Parcelable { @ServiceState.FrequencyRange int frequencyRange, int signalStrength, long elapsedRealtimeMs); + /** + * Returns the time in microseconds that the mobile radio has been actively transmitting data on + * a given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given + * transmission power level. + * + * @param rat Radio Access Technology {@see RadioAccessTechnology} + * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for + * RADIO_ACCESS_TECHNOLOGY_NR. Use + * {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access + * Technologies. + * @param signalStrength the cellular signal strength. {@see CellSignalStrength#getLevel()} + * @param elapsedRealtimeMs current elapsed realtime + * @return time (in milliseconds) the mobile radio spent actively transmitting data in the + * specified state, while on battery. Returns {@link DURATION_UNAVAILABLE} if + * data unavailable. + * @hide + */ + public abstract long getActiveTxRadioDurationMs(@RadioAccessTechnology int rat, + @ServiceState.FrequencyRange int frequencyRange, int signalStrength, + long elapsedRealtimeMs); + + /** + * Returns the time in microseconds that the mobile radio has been actively receiving data on a + * given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given + * transmission power level. + * + * @param rat Radio Access Technology {@see RadioAccessTechnology} + * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for + * RADIO_ACCESS_TECHNOLOGY_NR. Use + * {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access + * Technologies. + * @param elapsedRealtimeMs current elapsed realtime + * @return time (in milliseconds) the mobile radio spent actively receiving data in the + * specified state, while on battery. Returns {@link DURATION_UNAVAILABLE} if + * data unavailable. + * @hide + */ + public abstract long getActiveRxRadioDurationMs(@RadioAccessTechnology int rat, + @ServiceState.FrequencyRange int frequencyRange, long elapsedRealtimeMs); + static final String[] WIFI_SUPPL_STATE_NAMES = { "invalid", "disconn", "disabled", "inactive", "scanning", "authenticating", "associating", "associated", "4-way-handshake", @@ -2719,6 +2759,13 @@ public abstract class BatteryStats implements Parcelable { */ public static final long POWER_DATA_UNAVAILABLE = -1L; + /** + * Returned value if duration data is unavailable. + * + * {@hide} + */ + public static final long DURATION_UNAVAILABLE = -1L; + /** * Returns the battery consumption (in microcoulombs) of bluetooth, derived from on * device power measurement data. @@ -4093,6 +4140,10 @@ public abstract class BatteryStats implements Parcelable { " Mmwave frequency (greater than 6GHz):\n"}; final String signalStrengthHeader = " Signal Strength Time:\n"; + final String txHeader = + " Tx Time:\n"; + final String rxHeader = + " Rx Time: "; final String[] signalStrengthDescription = new String[]{ " unknown: ", " poor: ", @@ -4144,6 +4195,29 @@ public abstract class BatteryStats implements Parcelable { sb.append(")\n"); } + sb.append(prefix); + sb.append(txHeader); + for (int strength = 0; strength < numSignalStrength; strength++) { + final long timeMs = getActiveTxRadioDurationMs(rat, freqLvl, strength, + rawRealtimeMs); + if (timeMs <= 0) continue; + hasFreqData = true; + sb.append(prefix); + sb.append(signalStrengthDescription[strength]); + formatTimeMs(sb, timeMs); + sb.append("("); + sb.append(formatRatioLocked(timeMs, totalActiveTimesMs)); + sb.append(")\n"); + } + + sb.append(prefix); + sb.append(rxHeader); + final long rxTimeMs = getActiveRxRadioDurationMs(rat, freqLvl, rawRealtimeMs); + formatTimeMs(sb, rxTimeMs); + sb.append("("); + sb.append(formatRatioLocked(rxTimeMs, totalActiveTimesMs)); + sb.append(")\n"); + if (hasFreqData) { hasData = true; pw.print(sb); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 52539566351bc..6e8ec97d3d88d 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -166,7 +166,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 206; + static final int VERSION = 207; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -964,6 +964,16 @@ public class BatteryStatsImpl extends BatteryStats { * Timers for each combination of frequency range and signal strength. */ public final StopwatchTimer[][] perStateTimers; + /** + * Counters tracking the time (in milliseconds) spent transmitting data in a given state. + */ + @Nullable + private LongSamplingCounter[][] mPerStateTxDurationMs = null; + /** + * Counters tracking the time (in milliseconds) spent receiving data in at given frequency. + */ + @Nullable + private LongSamplingCounter[] mPerFrequencyRxDurationMs = null; RadioAccessTechnologyBatteryStats(int freqCount, Clock clock, TimeBase timeBase) { perStateTimers = @@ -1024,15 +1034,198 @@ public class BatteryStatsImpl extends BatteryStats { } /** - * Reset display timers. + * Returns the duration in milliseconds spent in a given state since the last mark. + */ + public long getTimeSinceMark(@ServiceState.FrequencyRange int frequencyRange, + int signalStrength, long elapsedRealtimeMs) { + return perStateTimers[frequencyRange][signalStrength].getTimeSinceMarkLocked( + elapsedRealtimeMs * 1000) / 1000; + } + + /** + * Set mark for all timers. + */ + public void setMark(long elapsedRealtimeMs) { + final int size = perStateTimers.length; + for (int i = 0; i < size; i++) { + for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) { + perStateTimers[i][j].setMark(elapsedRealtimeMs); + } + } + } + + /** + * Returns numbers of frequencies tracked for this RAT. + */ + public int getFrequencyRangeCount() { + return perStateTimers.length; + } + + /** + * Add TX time for a given state. + */ + public void incrementTxDuration(@ServiceState.FrequencyRange int frequencyRange, + int signalStrength, long durationMs) { + getTxDurationCounter(frequencyRange, signalStrength, true).addCountLocked(durationMs); + } + + /** + * Add TX time for a given frequency. + */ + public void incrementRxDuration(@ServiceState.FrequencyRange int frequencyRange, + long durationMs) { + getRxDurationCounter(frequencyRange, true).addCountLocked(durationMs); + } + + /** + * Reset radio access technology timers and counts. */ public void reset(long elapsedRealtimeUs) { final int size = perStateTimers.length; for (int i = 0; i < size; i++) { for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) { perStateTimers[i][j].reset(false, elapsedRealtimeUs); + if (mPerStateTxDurationMs == null) continue; + mPerStateTxDurationMs[i][j].reset(false, elapsedRealtimeUs); + } + if (mPerFrequencyRxDurationMs == null) continue; + mPerFrequencyRxDurationMs[i].reset(false, elapsedRealtimeUs); + } + } + + /** + * Write data to summary parcel + */ + public void writeSummaryToParcel(Parcel out, long elapsedRealtimeUs) { + final int freqCount = perStateTimers.length; + out.writeInt(freqCount); + out.writeInt(CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS); + for (int i = 0; i < freqCount; i++) { + for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) { + perStateTimers[i][j].writeSummaryFromParcelLocked(out, elapsedRealtimeUs); } } + + if (mPerStateTxDurationMs == null) { + out.writeInt(0); + } else { + out.writeInt(1); + for (int i = 0; i < freqCount; i++) { + for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) { + mPerStateTxDurationMs[i][j].writeSummaryFromParcelLocked(out); + } + } + } + + if (mPerFrequencyRxDurationMs == null) { + out.writeInt(0); + } else { + out.writeInt(1); + for (int i = 0; i < freqCount; i++) { + mPerFrequencyRxDurationMs[i].writeSummaryFromParcelLocked(out); + } + } + } + + /** + * Read data from summary parcel + */ + public void readSummaryFromParcel(Parcel in) { + final int oldFreqCount = in.readInt(); + final int oldSignalStrengthCount = in.readInt(); + final int currFreqCount = perStateTimers.length; + final int currSignalStrengthCount = CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; + + for (int freq = 0; freq < oldFreqCount; freq++) { + for (int strength = 0; strength < oldSignalStrengthCount; strength++) { + if (freq >= currFreqCount || strength >= currSignalStrengthCount) { + // Mismatch with the summary parcel. Consume the data but don't use it. + final StopwatchTimer temp = new StopwatchTimer(null, null, -1, null, + new TimeBase()); + // Consume perStateTimers data. + temp.readSummaryFromParcelLocked(in); + } else { + perStateTimers[freq][strength].readSummaryFromParcelLocked(in); + } + } + } + + if (in.readInt() == 1) { + for (int freq = 0; freq < oldFreqCount; freq++) { + for (int strength = 0; strength < oldSignalStrengthCount; strength++) { + if (freq >= currFreqCount || strength >= currSignalStrengthCount) { + // Mismatch with the summary parcel. Consume the data but don't use it. + final StopwatchTimer temp = new StopwatchTimer(null, null, -1, null, + new TimeBase()); + // Consume mPerStateTxDurationMs data. + temp.readSummaryFromParcelLocked(in); + } + getTxDurationCounter(freq, strength, true).readSummaryFromParcelLocked(in); + } + } + } + + if (in.readInt() == 1) { + for (int freq = 0; freq < oldFreqCount; freq++) { + if (freq >= currFreqCount) { + // Mismatch with the summary parcel. Consume the data but don't use it. + final StopwatchTimer + temp = new StopwatchTimer(null, null, -1, null, new TimeBase()); + // Consume mPerFrequencyRxDurationMs data. + temp.readSummaryFromParcelLocked(in); + continue; + } + getRxDurationCounter(freq, true).readSummaryFromParcelLocked(in); + } + } + } + + private LongSamplingCounter getTxDurationCounter( + @ServiceState.FrequencyRange int frequencyRange, int signalStrength, boolean make) { + if (mPerStateTxDurationMs == null) { + if (!make) return null; + + final int freqCount = getFrequencyRangeCount(); + final int signalStrengthCount = perStateTimers[0].length; + final TimeBase timeBase = perStateTimers[0][0].mTimeBase; + mPerStateTxDurationMs = new LongSamplingCounter[freqCount][signalStrengthCount]; + for (int freq = 0; freq < freqCount; freq++) { + for (int strength = 0; strength < signalStrengthCount; strength++) { + mPerStateTxDurationMs[freq][strength] = new LongSamplingCounter(timeBase); + } + } + } + if (frequencyRange < 0 || frequencyRange >= getFrequencyRangeCount()) { + Slog.w(TAG, "Unexpected frequency range (" + frequencyRange + + ") requested in getTxDurationCounter"); + return null; + } + if (signalStrength < 0 || signalStrength >= perStateTimers[0].length) { + Slog.w(TAG, "Unexpected signal strength (" + signalStrength + + ") requested in getTxDurationCounter"); + return null; + } + return mPerStateTxDurationMs[frequencyRange][signalStrength]; + } + + private LongSamplingCounter getRxDurationCounter( + @ServiceState.FrequencyRange int frequencyRange, boolean make) { + if (mPerFrequencyRxDurationMs == null) { + if (!make) return null; + + final int freqCount = getFrequencyRangeCount(); + final TimeBase timeBase = perStateTimers[0][0].mTimeBase; + mPerFrequencyRxDurationMs = new LongSamplingCounter[freqCount]; + for (int freq = 0; freq < freqCount; freq++) { + mPerFrequencyRxDurationMs[freq] = new LongSamplingCounter(timeBase); + } + } + if (frequencyRange < 0 || frequencyRange >= getFrequencyRangeCount()) { + Slog.w(TAG, "Unexpected frequency range (" + frequencyRange + + ") requested in getRxDurationCounter"); + return null; + } + return mPerFrequencyRxDurationMs[frequencyRange]; } } @@ -8000,6 +8193,32 @@ public class BatteryStatsImpl extends BatteryStats { elapsedRealtimeMs * 1000, STATS_SINCE_CHARGED) / 1000; } + @Override + public long getActiveTxRadioDurationMs(@RadioAccessTechnology int rat, + @ServiceState.FrequencyRange int frequencyRange, int signalStrength, + long elapsedRealtimeMs) { + final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat]; + if (stats == null) return DURATION_UNAVAILABLE; + + final LongSamplingCounter counter = stats.getTxDurationCounter(frequencyRange, + signalStrength, false); + if (counter == null) return DURATION_UNAVAILABLE; + + return counter.getCountLocked(STATS_SINCE_CHARGED); + } + + @Override + public long getActiveRxRadioDurationMs(@RadioAccessTechnology int rat, + @ServiceState.FrequencyRange int frequencyRange, long elapsedRealtimeMs) { + final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat]; + if (stats == null) return DURATION_UNAVAILABLE; + + final LongSamplingCounter counter = stats.getRxDurationCounter(frequencyRange, false); + if (counter == null) return DURATION_UNAVAILABLE; + + return counter.getCountLocked(STATS_SINCE_CHARGED); + } + @UnsupportedAppUsage @Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) { return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which); @@ -13482,6 +13701,67 @@ public class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mTmpRailStats.resetCellularTotalEnergyUsed(); } + + // Proportionally smear Rx and Tx times across each RAt + final int levelCount = CellSignalStrength.getNumSignalStrengthLevels(); + long[] perSignalStrengthActiveTimeMs = new long[levelCount]; + long totalActiveTimeMs = 0; + + for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) { + final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat]; + if (ratStats == null) continue; + + final int freqCount = ratStats.getFrequencyRangeCount(); + for (int freq = 0; freq < freqCount; freq++) { + for (int level = 0; level < levelCount; level++) { + final long durationMs = ratStats.getTimeSinceMark(freq, level, + elapsedRealtimeMs); + perSignalStrengthActiveTimeMs[level] += durationMs; + totalActiveTimeMs += durationMs; + } + } + } + + if (totalActiveTimeMs != 0) { + // Smear the provided Tx/Rx durations across each RAT, frequency, and signal + // strength. + for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) { + final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat]; + if (ratStats == null) continue; + + final int freqCount = ratStats.getFrequencyRangeCount(); + for (int freq = 0; freq < freqCount; freq++) { + long frequencyDurationMs = 0; + for (int level = 0; level < levelCount; level++) { + final long durationMs = ratStats.getTimeSinceMark(freq, level, + elapsedRealtimeMs); + final long totalLvlDurationMs = + perSignalStrengthActiveTimeMs[level]; + if (totalLvlDurationMs == 0) continue; + final long totalTxLvlDurations = + deltaInfo.getTransmitDurationMillisAtPowerLevel(level); + // Smear HAL provided Tx power level duration based on active modem + // duration in a given state. (Add totalLvlDurationMs / 2 before + // the integer division with totalLvlDurationMs for rounding.) + final long proportionalTxDurationMs = + (durationMs * totalTxLvlDurations + + (totalLvlDurationMs / 2)) / totalLvlDurationMs; + ratStats.incrementTxDuration(freq, level, proportionalTxDurationMs); + frequencyDurationMs += durationMs; + } + final long totalRxDuration = deltaInfo.getReceiveTimeMillis(); + // Smear HAL provided Rx power duration based on active modem + // duration in a given state. (Add totalActiveTimeMs / 2 before the + // integer division with totalActiveTimeMs for rounding.) + final long proportionalRxDurationMs = + (frequencyDurationMs * totalRxDuration + (totalActiveTimeMs + / 2)) / totalActiveTimeMs; + ratStats.incrementRxDuration(freq, proportionalRxDurationMs); + } + + ratStats.setMark(elapsedRealtimeMs); + } + } } long totalAppRadioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000); @@ -16926,6 +17206,13 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in); mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in); } + + final int numRat = in.readInt(); + for (int i = 0; i < numRat; i++) { + if (in.readInt() == 0) continue; + getRatBatteryStatsLocked(i).readSummaryFromParcel(in); + } + mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; mMobileRadioActiveTimer.readSummaryFromParcelLocked(in); mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in); @@ -17431,6 +17718,17 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out); mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out); } + final int numRat = mPerRatBatteryStats.length; + out.writeInt(numRat); + for (int i = 0; i < numRat; i++) { + final RadioAccessTechnologyBatteryStats ratStat = mPerRatBatteryStats[i]; + if (ratStat == null) { + out.writeInt(0); + continue; + } + out.writeInt(1); + ratStat.writeSummaryToParcel(out, nowRealtime); + } mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, nowRealtime); mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, nowRealtime); mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index f5cbffb64bb55..7ccb9d963ee62 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -17,6 +17,8 @@ package com.android.internal.os; import static android.os.BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; +import static android.os.BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; import static android.os.BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR; import static android.os.BatteryStats.STATS_SINCE_CHARGED; import static android.os.BatteryStats.WAKE_TYPE_PARTIAL; @@ -24,7 +26,10 @@ import static android.os.BatteryStats.WAKE_TYPE_PARTIAL; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY; +import static org.mockito.Mockito.mock; + import android.app.ActivityManager; +import android.app.usage.NetworkStatsManager; import android.os.BatteryStats; import android.os.BatteryStats.HistoryItem; import android.os.BatteryStats.Uid.Sensor; @@ -34,6 +39,7 @@ import android.os.WorkSource; import android.telephony.Annotation; import android.telephony.CellSignalStrength; import android.telephony.DataConnectionRealTimeInfo; +import android.telephony.ModemActivityInfo; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.util.SparseIntArray; @@ -48,6 +54,8 @@ import com.android.internal.power.MeasuredEnergyStats; import junit.framework.TestCase; +import org.mockito.Mock; + import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -72,6 +80,13 @@ public class BatteryStatsNoteTest extends TestCase { private static final int ISOLATED_UID = UserHandle.getUid(0, ISOLATED_APP_ID); private static final WorkSource WS = new WorkSource(UID); + enum ModemState { + SLEEP, IDLE, RECEIVING, TRANSMITTING + } + + @Mock + NetworkStatsManager mNetworkStatsManager; + /** * Test BatteryStatsImpl.Uid.noteBluetoothScanResultLocked. */ @@ -1173,69 +1188,29 @@ public class BatteryStatsNoteTest extends TestCase { } @SmallTest - public void testGetPerStateActiveRadioDurationMs() { + public void testGetPerStateActiveRadioDurationMs_noModemActivity() { final MockClock clock = new MockClock(); // holds realtime and uptime in ms final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock); - final int ratCount = BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; + final int ratCount = RADIO_ACCESS_TECHNOLOGY_COUNT; final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1; final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels(); final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount]; + final long[][] expectedRxDurationsMs = new long[ratCount][frequencyCount]; + final long[][][] expectedTxDurationsMs = new long[ratCount][frequencyCount][txLevelCount]; for (int rat = 0; rat < ratCount; rat++) { for (int freq = 0; freq < frequencyCount; freq++) { + // Should have no RX data without Modem Activity Info + expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE; for (int txLvl = 0; txLvl < txLevelCount; txLvl++) { expectedDurationsMs[rat][freq][txLvl] = 0; + // Should have no TX data without Modem Activity Info + expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE; } } } - class ModemAndBatteryState { - public long currentTimeMs = 100; - public boolean onBattery = false; - public boolean modemActive = false; - @Annotation.NetworkType - public int currentNetworkDataType = TelephonyManager.NETWORK_TYPE_UNKNOWN; - @BatteryStats.RadioAccessTechnology - public int currentRat = BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER; - @ServiceState.FrequencyRange - public int currentFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; - public SparseIntArray currentSignalStrengths = new SparseIntArray(); - - void setOnBattery(boolean onBattery) { - this.onBattery = onBattery; - bi.updateTimeBasesLocked(onBattery, Display.STATE_OFF, currentTimeMs * 1000, - currentTimeMs * 1000); - } - - void setModemActive(boolean active) { - modemActive = active; - final int state = active ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH - : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; - bi.noteMobileRadioPowerStateLocked(state, currentTimeMs * 1000_000L, UID); - } - - void setRatType(@Annotation.NetworkType int dataType, - @BatteryStats.RadioAccessTechnology int rat) { - currentNetworkDataType = dataType; - currentRat = rat; - bi.notePhoneDataConnectionStateLocked(dataType, true, ServiceState.STATE_IN_SERVICE, - currentFrequencyRange); - } - - void setFrequencyRange(@ServiceState.FrequencyRange int frequency) { - currentFrequencyRange = frequency; - bi.notePhoneDataConnectionStateLocked(currentNetworkDataType, true, - ServiceState.STATE_IN_SERVICE, frequency); - } - - void setSignalStrength(@BatteryStats.RadioAccessTechnology int rat, int strength) { - currentSignalStrengths.put(rat, strength); - final int size = currentSignalStrengths.size(); - final int newestGenSignalStrength = currentSignalStrengths.valueAt(size - 1); - bi.notePhoneSignalStrengthLocked(newestGenSignalStrength, currentSignalStrengths); - } - } - final ModemAndBatteryState state = new ModemAndBatteryState(); + final ModemAndBatteryState state = new ModemAndBatteryState(bi, null); IntConsumer incrementTime = inc -> { state.currentTimeMs += inc; @@ -1253,6 +1228,7 @@ public class BatteryStatsNoteTest extends TestCase { expectedDurationsMs[currentRat][currentFrequencyRange][currentSignalStrength] += inc; }; + state.setOnBattery(false); state.setModemActive(false); state.setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN, @@ -1260,95 +1236,367 @@ public class BatteryStatsNoteTest extends TestCase { state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_UNKNOWN); state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER, CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // While not on battery, the timers should not increase. state.setModemActive(true); incrementTime.accept(100); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR); incrementTime.accept(200); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR, CellSignalStrength.SIGNAL_STRENGTH_GOOD); incrementTime.accept(500); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_MMWAVE); incrementTime.accept(300); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setRatType(TelephonyManager.NETWORK_TYPE_LTE, BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE); incrementTime.accept(400); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, CellSignalStrength.SIGNAL_STRENGTH_MODERATE); incrementTime.accept(500); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // When set on battery, currently active state (RAT:LTE, Signal Strength:Moderate) should // start counting up. state.setOnBattery(true); incrementTime.accept(600); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); - + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // Changing LTE signal strength should be tracked. state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, CellSignalStrength.SIGNAL_STRENGTH_POOR); incrementTime.accept(700); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); incrementTime.accept(800); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, CellSignalStrength.SIGNAL_STRENGTH_GOOD); incrementTime.accept(900); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, CellSignalStrength.SIGNAL_STRENGTH_GREAT); incrementTime.accept(1000); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // Change in the signal strength of nonactive RAT should not affect anything. state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER, CellSignalStrength.SIGNAL_STRENGTH_POOR); incrementTime.accept(1100); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // Changing to OTHER Rat should start tracking the poor signal strength. state.setRatType(TelephonyManager.NETWORK_TYPE_CDMA, BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER); incrementTime.accept(1200); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // Noting frequency change should not affect non NR Rat. state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_HIGH); incrementTime.accept(1300); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // Now the NR Rat, HIGH frequency range, good signal strength should start counting. state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR); incrementTime.accept(1400); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // Noting frequency change should not affect non NR Rat. state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW); incrementTime.accept(1500); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); // Modem no longer active, should not be tracking any more. state.setModemActive(false); incrementTime.accept(1500); - checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + } + @SmallTest + public void testGetPerStateActiveRadioDurationMs_withModemActivity() { + final MockClock clock = new MockClock(); // holds realtime and uptime in ms + final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock); + bi.setPowerProfile(mock(PowerProfile.class)); + final int ratCount = RADIO_ACCESS_TECHNOLOGY_COUNT; + final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1; + final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels(); + + final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount]; + final long[][] expectedRxDurationsMs = new long[ratCount][frequencyCount]; + final long[][][] expectedTxDurationsMs = new long[ratCount][frequencyCount][txLevelCount]; + for (int rat = 0; rat < ratCount; rat++) { + for (int freq = 0; freq < frequencyCount; freq++) { + if (rat != RADIO_ACCESS_TECHNOLOGY_NR + && freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) { + // Only the NR RAT should have per frequency data. + expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE; + } else { + expectedRxDurationsMs[rat][freq] = 0; + } + expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE; + + for (int txLvl = 0; txLvl < txLevelCount; txLvl++) { + expectedDurationsMs[rat][freq][txLvl] = 0; + + if (rat != RADIO_ACCESS_TECHNOLOGY_NR + && freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) { + // Only the NR RAT should have per frequency data. + expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE; + } else { + expectedTxDurationsMs[rat][freq][txLvl] = 0; + } + expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE; + } + } + } + + final ModemActivityInfo mai = new ModemActivityInfo(0L, 0L, 0L, new int[txLevelCount], 0L); + final ModemAndBatteryState state = new ModemAndBatteryState(bi, mai); + + IntConsumer incrementTime = inc -> { + state.currentTimeMs += inc; + clock.realtime = clock.uptime = state.currentTimeMs; + + // If the device is not on battery, no timers should increment. + if (!state.onBattery) return; + // If the modem is not active, no timers should increment. + if (!state.modemActive) return; + + final int currRat = state.currentRat; + final int currFreqRange = + currRat == RADIO_ACCESS_TECHNOLOGY_NR ? state.currentFrequencyRange : 0; + int currSignalStrength = state.currentSignalStrengths.get(currRat); + + expectedDurationsMs[currRat][currFreqRange][currSignalStrength] += inc; + + // Evaluate the HAL provided time in states. + switch (state.modemState) { + case SLEEP: + long sleepMs = state.modemActivityInfo.getSleepTimeMillis(); + state.modemActivityInfo.setSleepTimeMillis(sleepMs + inc); + break; + case IDLE: + long idleMs = state.modemActivityInfo.getIdleTimeMillis(); + state.modemActivityInfo.setIdleTimeMillis(idleMs + inc); + break; + case RECEIVING: + long rxMs = state.modemActivityInfo.getReceiveTimeMillis(); + state.modemActivityInfo.setReceiveTimeMillis(rxMs + inc); + expectedRxDurationsMs[currRat][currFreqRange] += inc; + break; + case TRANSMITTING: + int[] txMs = state.modemActivityInfo.getTransmitTimeMillis(); + txMs[currSignalStrength] += inc; + state.modemActivityInfo.setTransmitTimeMillis(txMs); + expectedTxDurationsMs[currRat][currFreqRange][currSignalStrength] += inc; + break; + } + }; + + state.setOnBattery(false); + state.setModemActive(false); + state.setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN, + BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER); + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_UNKNOWN); + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER, + CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // While not on battery, the timers should not increase. + state.setModemActive(true); + incrementTime.accept(100); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR); + incrementTime.accept(200); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR, + CellSignalStrength.SIGNAL_STRENGTH_GOOD); + incrementTime.accept(500); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_MMWAVE); + incrementTime.accept(300); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setRatType(TelephonyManager.NETWORK_TYPE_LTE, + BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE); + incrementTime.accept(400); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_MODERATE); + incrementTime.accept(500); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // Data will now be available. + for (int rat = 0; rat < ratCount; rat++) { + for (int freq = 0; freq < frequencyCount; freq++) { + if (rat == RADIO_ACCESS_TECHNOLOGY_NR + || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) { + // Only the NR RAT should have per frequency data. + expectedRxDurationsMs[rat][freq] = 0; + } + for (int txLvl = 0; txLvl < txLevelCount; txLvl++) { + if (rat == RADIO_ACCESS_TECHNOLOGY_NR + || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) { + // Only the NR RAT should have per frequency data. + expectedTxDurationsMs[rat][freq][txLvl] = 0; + } + } + } + } + + // When set on battery, currently active state (RAT:LTE, Signal Strength:Moderate) should + // start counting up. + state.setOnBattery(true); + incrementTime.accept(300); + state.setModemState(ModemState.RECEIVING); + incrementTime.accept(500); + state.setModemState(ModemState.TRANSMITTING); + incrementTime.accept(600); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + // Changing LTE signal strength should be tracked. + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_POOR); + incrementTime.accept(300); + state.setModemState(ModemState.SLEEP); + incrementTime.accept(1000); + state.setModemState(ModemState.RECEIVING); + incrementTime.accept(700); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + incrementTime.accept(800); + state.setModemState(ModemState.TRANSMITTING); + incrementTime.accept(222); + state.setModemState(ModemState.IDLE); + incrementTime.accept(111); + state.setModemState(ModemState.RECEIVING); + incrementTime.accept(7777); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_GOOD); + incrementTime.accept(88); + state.setModemState(ModemState.TRANSMITTING); + incrementTime.accept(900); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_GREAT); + incrementTime.accept(123); + state.setModemState(ModemState.RECEIVING); + incrementTime.accept(333); + state.setModemState(ModemState.TRANSMITTING); + incrementTime.accept(1000); + state.setModemState(ModemState.RECEIVING); + incrementTime.accept(555); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // Change in the signal strength of nonactive RAT should not affect anything. + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER, + CellSignalStrength.SIGNAL_STRENGTH_POOR); + incrementTime.accept(631); + state.setModemState(ModemState.TRANSMITTING); + incrementTime.accept(321); + state.setModemState(ModemState.RECEIVING); + incrementTime.accept(99); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // Changing to OTHER Rat should start tracking the poor signal strength. + state.setRatType(TelephonyManager.NETWORK_TYPE_CDMA, + BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER); + incrementTime.accept(1200); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // Noting frequency change should not affect non NR Rat. + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_HIGH); + incrementTime.accept(444); + state.setModemState(ModemState.TRANSMITTING); + incrementTime.accept(1300); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // Now the NR Rat, HIGH frequency range, good signal strength should start counting. + state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR); + incrementTime.accept(1400); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // Frequency changed to low. + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW); + incrementTime.accept(852); + state.setModemState(ModemState.RECEIVING); + incrementTime.accept(157); + state.setModemState(ModemState.TRANSMITTING); + incrementTime.accept(1500); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); + + // Modem no longer active, should not be tracking any more. + state.setModemActive(false); + incrementTime.accept(1500); + state.noteModemControllerActivity(); + checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs, + expectedTxDurationsMs, bi, state.currentTimeMs); } private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) { @@ -1426,28 +1674,124 @@ public class BatteryStatsNoteTest extends TestCase { } private void checkPerStateActiveRadioDurations(long[][][] expectedDurationsMs, + long[][] expectedRxDurationsMs, long[][][] expectedTxDurationsMs, BatteryStatsImpl bi, long currentTimeMs) { for (int rat = 0; rat < expectedDurationsMs.length; rat++) { final long[][] expectedRatDurationsMs = expectedDurationsMs[rat]; for (int freq = 0; freq < expectedRatDurationsMs.length; freq++) { + final long expectedRxDurationMs = expectedRxDurationsMs[rat][freq]; + + // Build a verbose fail message, just in case. + final StringBuilder rxFailSb = new StringBuilder(); + rxFailSb.append("Wrong time in Rx state for RAT:"); + rxFailSb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]); + rxFailSb.append(", frequency:"); + rxFailSb.append(ServiceState.frequencyRangeToString(freq)); + assertEquals(rxFailSb.toString(), expectedRxDurationMs, + bi.getActiveRxRadioDurationMs(rat, freq, currentTimeMs)); + final long[] expectedFreqDurationsMs = expectedRatDurationsMs[freq]; for (int strength = 0; strength < expectedFreqDurationsMs.length; strength++) { final long expectedSignalStrengthDurationMs = expectedFreqDurationsMs[strength]; + final long expectedTxDurationMs = expectedTxDurationsMs[rat][freq][strength]; final long actualDurationMs = bi.getActiveRadioDurationMs(rat, freq, strength, currentTimeMs); - // Build a verbose fail message, just in case. - final StringBuilder sb = new StringBuilder(); - sb.append("Wrong time in state for RAT:"); - sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]); - sb.append(", frequency:"); - sb.append(ServiceState.frequencyRangeToString(freq)); - sb.append(", strength:"); - sb.append(strength); + final StringBuilder failSb = new StringBuilder(); + failSb.append("Wrong time in state for RAT:"); + failSb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]); + failSb.append(", frequency:"); + failSb.append(ServiceState.frequencyRangeToString(freq)); + failSb.append(", strength:"); + failSb.append(strength); + assertEquals(failSb.toString(), expectedSignalStrengthDurationMs, + actualDurationMs); - assertEquals(sb.toString(), expectedSignalStrengthDurationMs, actualDurationMs); + final StringBuilder txFailSb = new StringBuilder(); + txFailSb.append("Wrong time in Tx state for RAT:"); + txFailSb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]); + txFailSb.append(", frequency:"); + txFailSb.append(ServiceState.frequencyRangeToString(freq)); + txFailSb.append(", strength:"); + txFailSb.append(strength); + assertEquals(txFailSb.toString(), expectedTxDurationMs, + bi.getActiveTxRadioDurationMs(rat, freq, strength, currentTimeMs)); } } } } + + private class ModemAndBatteryState { + public long currentTimeMs = 100; + public boolean onBattery = false; + public boolean modemActive = false; + @Annotation.NetworkType + public int currentNetworkDataType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + @BatteryStats.RadioAccessTechnology + public int currentRat = BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER; + @ServiceState.FrequencyRange + public int currentFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; + public SparseIntArray currentSignalStrengths = new SparseIntArray(); + public ModemState modemState = ModemState.SLEEP; + public ModemActivityInfo modemActivityInfo; + + private final MockBatteryStatsImpl mBsi; + + ModemAndBatteryState(MockBatteryStatsImpl bsi, ModemActivityInfo mai) { + mBsi = bsi; + modemActivityInfo = mai; + } + + void setOnBattery(boolean onBattery) { + this.onBattery = onBattery; + mBsi.updateTimeBasesLocked(onBattery, Display.STATE_OFF, currentTimeMs * 1000, + currentTimeMs * 1000); + mBsi.setOnBatteryInternal(onBattery); + noteModemControllerActivity(); + } + + void setModemActive(boolean active) { + modemActive = active; + final int state = active ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH + : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; + mBsi.noteMobileRadioPowerStateLocked(state, currentTimeMs * 1000_000L, UID); + noteModemControllerActivity(); + } + + void setRatType(@Annotation.NetworkType int dataType, + @BatteryStats.RadioAccessTechnology int rat) { + currentNetworkDataType = dataType; + currentRat = rat; + mBsi.notePhoneDataConnectionStateLocked(dataType, true, ServiceState.STATE_IN_SERVICE, + currentFrequencyRange); + } + + void setFrequencyRange(@ServiceState.FrequencyRange int frequency) { + currentFrequencyRange = frequency; + mBsi.notePhoneDataConnectionStateLocked(currentNetworkDataType, true, + ServiceState.STATE_IN_SERVICE, frequency); + } + + void setSignalStrength(@BatteryStats.RadioAccessTechnology int rat, int strength) { + currentSignalStrengths.put(rat, strength); + final int size = currentSignalStrengths.size(); + final int newestGenSignalStrength = currentSignalStrengths.valueAt(size - 1); + mBsi.notePhoneSignalStrengthLocked(newestGenSignalStrength, currentSignalStrengths); + } + + void setModemState(ModemState state) { + modemState = state; + } + + void noteModemControllerActivity() { + if (modemActivityInfo == null) return; + modemActivityInfo.setTimestamp(currentTimeMs); + ModemActivityInfo copy = new ModemActivityInfo(modemActivityInfo.getTimestampMillis(), + modemActivityInfo.getSleepTimeMillis(), modemActivityInfo.getIdleTimeMillis(), + modemActivityInfo.getTransmitTimeMillis().clone(), + modemActivityInfo.getReceiveTimeMillis()); + mBsi.noteModemControllerActivity(copy, POWER_DATA_UNAVAILABLE, + currentTimeMs, currentTimeMs, mNetworkStatsManager); + } + } }