diff options
8 files changed, 974 insertions, 174 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 29884b132e5a..dd11f68ffe50 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -24,14 +24,12 @@ import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Predicate; import android.content.Context; import android.content.pm.ApplicationInfo; import android.telephony.SignalStrength; import android.text.format.DateFormat; import android.util.ArrayMap; -import android.util.Log; import android.util.LongSparseArray; import android.util.MutableBoolean; import android.util.Pair; @@ -184,7 +182,7 @@ public abstract class BatteryStats implements Parcelable { * New in version 19: * - Wakelock data (wl) gets current and max times. * New in version 20: - * - Sensor gets a background counter. + * - Sensor, BluetoothScan, WifiScan get background timers and counter. */ static final String CHECKIN_VERSION = "20"; @@ -363,7 +361,7 @@ public abstract class BatteryStats implements Parcelable { /** * Returns the max duration if it is being tracked. - * Not all Timer subclasses track the max duration and the current duration. + * Not all Timer subclasses track the max, total, current durations. */ public long getMaxDurationMsLocked(long elapsedRealtimeMs) { @@ -372,13 +370,28 @@ public abstract class BatteryStats implements Parcelable { /** * Returns the current time the timer has been active, if it is being tracked. - * Not all Timer subclasses track the max duration and the current duration. + * Not all Timer subclasses track the max, total, current durations. */ public long getCurrentDurationMsLocked(long elapsedRealtimeMs) { return -1; } /** + * Returns the current time the timer has been active, if it is being tracked. + * + * Returns the total cumulative duration (i.e. sum of past durations) that this timer has + * been on since reset. + * This may differ from getTotalTimeLocked(elapsedRealtimeUs, STATS_SINCE_CHARGED)/1000 since, + * depending on the Timer, getTotalTimeLocked may represent the total 'blamed' or 'pooled' + * time, rather than the actual time. By contrast, getTotalDurationMsLocked always gives + * the actual total time. + * Not all Timer subclasses track the max, total, current durations. + */ + public long getTotalDurationMsLocked(long elapsedRealtimeMs) { + return -1; + } + + /** * Returns whether the timer is currently running. Some types of timers * (e.g. BatchTimers) don't know whether the event is currently active, * and report false. @@ -477,6 +490,9 @@ public abstract class BatteryStats implements Parcelable { public abstract long getFullWifiLockTime(long elapsedRealtimeUs, int which); public abstract long getWifiScanTime(long elapsedRealtimeUs, int which); public abstract int getWifiScanCount(int which); + public abstract int getWifiScanBackgroundCount(int which); + public abstract long getWifiScanActualTime(long elapsedRealtimeUs); + public abstract long getWifiScanBackgroundTime(long elapsedRealtimeUs); public abstract long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which); public abstract int getWifiBatchedScanCount(int csphBin, int which); public abstract long getWifiMulticastTime(long elapsedRealtimeUs, int which); @@ -486,6 +502,7 @@ public abstract class BatteryStats implements Parcelable { public abstract Timer getCameraTurnedOnTimer(); public abstract Timer getForegroundActivityTimer(); public abstract Timer getBluetoothScanTimer(); + public abstract Timer getBluetoothScanBackgroundTimer(); // Note: the following times are disjoint. They can be added together to find the // total time a uid has had any processes running at all. @@ -609,8 +626,8 @@ public abstract class BatteryStats implements Parcelable { public abstract Timer getSensorTime(); - /** Returns a counter for usage count when in the background. */ - public abstract Counter getSensorBgCount(); + /** Returns a Timer for sensor usage when app is in the background. */ + public abstract Timer getSensorBackgroundTime(); } public class Pid { @@ -2652,7 +2669,7 @@ public abstract class BatteryStats implements Parcelable { * @param pw a PrintWriter object to print to. * @param sb a StringBuilder object. * @param timer a Timer object contining the wakelock times. - * @param rawRealtime the current on-battery time in microseconds. + * @param rawRealtimeUs the current on-battery time in microseconds. * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. * @param prefix a String to be prepended to each line of output. * @param type the name of the timer. @@ -3284,19 +3301,41 @@ public abstract class BatteryStats implements Parcelable { final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which); final long wifiScanTime = u.getWifiScanTime(rawRealtime, which); final int wifiScanCount = u.getWifiScanCount(which); + final int wifiScanCountBg = u.getWifiScanBackgroundCount(which); + // Note that 'ActualTime' are unpooled and always since reset (regardless of 'which') + final long wifiScanActualTime = u.getWifiScanActualTime(rawRealtime); + final long wifiScanActualTimeBg = u.getWifiScanBackgroundTime(rawRealtime); final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which); if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0 + || wifiScanCountBg != 0 || wifiScanActualTime != 0 || wifiScanActualTimeBg != 0 || uidWifiRunningTime != 0) { dumpLine(pw, uid, category, WIFI_DATA, fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime, wifiScanCount, - /* legacy fields follow, keep at 0 */ 0, 0, 0); + /* legacy fields follow, keep at 0 */ 0, 0, 0, + wifiScanCountBg, wifiScanActualTime, wifiScanActualTimeBg); } dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA, u.getWifiControllerActivity(), which); - dumpTimer(pw, uid, category, BLUETOOTH_MISC_DATA, u.getBluetoothScanTimer(), - rawRealtime, which); + final Timer bleTimer = u.getBluetoothScanTimer(); + if (bleTimer != null) { + // Convert from microseconds to milliseconds with rounding + final long totalTime = (bleTimer.getTotalTimeLocked(rawRealtime, which) + 500) + / 1000; + if (totalTime != 0) { + final int count = bleTimer.getCountLocked(which); + final Timer bleTimerBg = u.getBluetoothScanBackgroundTimer(); + final int countBg = bleTimerBg != null ? bleTimerBg.getCountLocked(which) : 0; + final long rawRealtimeMs = (rawRealtime + 500) / 1000; + // 'actualTime' are unpooled and always since reset (regardless of 'which') + final long actualTime = bleTimer.getTotalDurationMsLocked(rawRealtimeMs); + final long actualTimeBg = bleTimerBg != null ? + bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0; + dumpLine(pw, uid, category, BLUETOOTH_MISC_DATA, totalTime, count, + countBg, actualTime, actualTimeBg); + } + } dumpControllerActivityLine(pw, uid, category, BLUETOOTH_CONTROLLER_DATA, u.getBluetoothControllerActivity(), which); @@ -3375,16 +3414,21 @@ public abstract class BatteryStats implements Parcelable { final Uid.Sensor se = sensors.valueAt(ise); final int sensorNumber = sensors.keyAt(ise); final Timer timer = se.getSensorTime(); - final Counter bgCounter = se.getSensorBgCount(); if (timer != null) { // Convert from microseconds to milliseconds with rounding final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; - final int count = timer.getCountLocked(which); - final int bgCount = bgCounter != null ? bgCounter.getCountLocked(which) : 0; if (totalTime != 0) { - dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count, - bgCount); + final int count = timer.getCountLocked(which); + final Timer bgTimer = se.getSensorBackgroundTime(); + final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : 0; + final long rawRealtimeMs = (rawRealtime + 500) / 1000; + // 'actualTime' are unpooled and always since reset (regardless of 'which') + final long actualTime = timer.getTotalDurationMsLocked(rawRealtimeMs); + final long bgActualTime = bgTimer != null ? + bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0; + dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, + count, bgCount, actualTime, bgActualTime); } } } @@ -4294,6 +4338,10 @@ public abstract class BatteryStats implements Parcelable { final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which); final long wifiScanTime = u.getWifiScanTime(rawRealtime, which); final int wifiScanCount = u.getWifiScanCount(which); + final int wifiScanCountBg = u.getWifiScanBackgroundCount(which); + // 'actualTime' are unpooled and always since reset (regardless of 'which') + final long wifiScanActualTime = u.getWifiScanActualTime(rawRealtime); + final long wifiScanActualTimeBg = u.getWifiScanBackgroundTime(rawRealtime); final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which); final long mobileWakeup = u.getMobileRadioApWakeupCount(which); @@ -4344,6 +4392,7 @@ public abstract class BatteryStats implements Parcelable { } if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0 + || wifiScanCountBg != 0 || wifiScanActualTime != 0 || wifiScanActualTimeBg != 0 || uidWifiRunningTime != 0) { sb.setLength(0); sb.append(prefix); sb.append(" Wifi Running: "); @@ -4354,11 +4403,26 @@ public abstract class BatteryStats implements Parcelable { formatTimeMs(sb, fullWifiLockOnTime / 1000); sb.append("("); sb.append(formatRatioLocked(fullWifiLockOnTime, whichBatteryRealtime)); sb.append(")\n"); - sb.append(prefix); sb.append(" Wifi Scan: "); + sb.append(prefix); sb.append(" Wifi Scan (blamed): "); formatTimeMs(sb, wifiScanTime / 1000); sb.append("("); sb.append(formatRatioLocked(wifiScanTime, whichBatteryRealtime)); sb.append(") "); sb.append(wifiScanCount); + sb.append("x\n"); + // actual and background times are unpooled and since reset (regardless of 'which') + sb.append(prefix); sb.append(" Wifi Scan (actual): "); + formatTimeMs(sb, wifiScanActualTime / 1000); + sb.append("("); sb.append(formatRatioLocked(wifiScanActualTime, + computeBatteryRealtime(rawRealtime, STATS_SINCE_CHARGED))); + sb.append(") "); + sb.append(wifiScanCount); + sb.append("x\n"); + sb.append(prefix); sb.append(" Background Wifi Scan: "); + formatTimeMs(sb, wifiScanActualTimeBg / 1000); + sb.append("("); sb.append(formatRatioLocked(wifiScanActualTimeBg, + computeBatteryRealtime(rawRealtime, STATS_SINCE_CHARGED))); + sb.append(") "); + sb.append(wifiScanCountBg); sb.append("x"); pw.println(sb.toString()); } @@ -4381,8 +4445,50 @@ public abstract class BatteryStats implements Parcelable { pw.println(" sent"); } - uidActivity |= printTimer(pw, sb, u.getBluetoothScanTimer(), rawRealtime, which, prefix, - "Bluetooth Scan"); + final Timer bleTimer = u.getBluetoothScanTimer(); + if (bleTimer != null) { + // Convert from microseconds to milliseconds with rounding + final long totalTimeMs = (bleTimer.getTotalTimeLocked(rawRealtime, which) + 500) + / 1000; + if (totalTimeMs != 0) { + final int count = bleTimer.getCountLocked(which); + final Timer bleTimerBg = u.getBluetoothScanBackgroundTimer(); + final int countBg = bleTimerBg != null ? bleTimerBg.getCountLocked(which) : 0; + final long rawRealtimeMs = (rawRealtime + 500) / 1000; + // 'actualTime' are unpooled and always since reset (regardless of 'which') + final long actualTimeMs = bleTimer.getTotalDurationMsLocked(rawRealtimeMs); + final long actualTimeMsBg = bleTimerBg != null ? + bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0; + + sb.setLength(0); + sb.append(prefix); + sb.append(" "); + sb.append("Bluetooth Scan"); + sb.append(": "); + if (actualTimeMs != totalTimeMs) { + formatTimeMs(sb, totalTimeMs); + sb.append("blamed realtime, "); + } + formatTimeMs(sb, actualTimeMs); // since reset, regardless of 'which' + sb.append("realtime ("); + sb.append(count); + sb.append(" times)"); + if (bleTimer.isRunningLocked()) { + sb.append(" (running)"); + } + if (actualTimeMsBg != 0 || countBg > 0) { + sb.append(", "); + formatTimeMs(sb, actualTimeMsBg); // since reset, regardless of 'which' + sb.append("background ("); + sb.append(countBg); + sb.append(" times)"); + } + pw.println(sb.toString()); + uidActivity = true; + } + } + + if (u.hasUserActivity()) { boolean hasData = false; @@ -4553,25 +4659,38 @@ public abstract class BatteryStats implements Parcelable { sb.append(": "); final Timer timer = se.getSensorTime(); - final Counter bgCounter = se.getSensorBgCount(); if (timer != null) { // Convert from microseconds to milliseconds with rounding - final long totalTime = (timer.getTotalTimeLocked( - rawRealtime, which) + 500) / 1000; + final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) + / 1000; final int count = timer.getCountLocked(which); - final int bgCount = bgCounter != null ? bgCounter.getCountLocked(which) : 0; + final Timer bgTimer = se.getSensorBackgroundTime(); + final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : 0; + final long rawRealtimeMs = (rawRealtime + 500) / 1000; + // 'actualTime' are unpooled and always since reset (regardless of 'which') + final long actualTime = timer.getTotalDurationMsLocked(rawRealtimeMs); + final long bgActualTime = bgTimer != null ? + bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0; + //timer.logState(); if (totalTime != 0) { - formatTimeMs(sb, totalTime); + if (actualTime != totalTime) { + formatTimeMs(sb, totalTime); + sb.append("blamed realtime, "); + } + + formatTimeMs(sb, actualTime); // since reset, regardless of 'which' sb.append("realtime ("); sb.append(count); - sb.append(" times"); - if (bgCount > 0) { + sb.append(" times)"); + + if (bgActualTime != 0 || bgCount > 0) { sb.append(", "); + formatTimeMs(sb, bgActualTime); // since reset, regardless of 'which' + sb.append("background ("); sb.append(bgCount); - sb.append(" bg"); + sb.append(" times)"); } - sb.append(")"); } else { sb.append("(not used)"); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 6aa77665db51..a682f956cc0a 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -479,7 +479,8 @@ public class BatteryStatsImpl extends BatteryStats { new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS]; int mBluetoothScanNesting; - StopwatchTimer mBluetoothScanTimer; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected StopwatchTimer mBluetoothScanTimer; int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; long mMobileRadioActiveStartTime; @@ -1586,19 +1587,28 @@ public class BatteryStatsImpl extends BatteryStats { long mStartTimeMs = -1; /** - * The longest time period (in ms) that the timer has been active. + * The longest time period (in ms) that the timer has been active. Not pooled. */ long mMaxDurationMs; /** - * The total time (in ms) that that the timer has been active since reset(). + * The time (in ms) that that the timer has been active since most recent + * stopRunningLocked() or reset(). Not pooled. */ long mCurrentDurationMs; + /** + * The total time (in ms) that that the timer has been active since most recent reset() + * prior to the current startRunningLocked. This is the sum of all past currentDurations + * (but not including the present currentDuration) since reset. Not pooled. + */ + long mTotalDurationMs; + public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool, TimeBase timeBase, Parcel in) { super(clocks, uid, type, timerPool, timeBase, in); mMaxDurationMs = in.readLong(); + mTotalDurationMs = in.readLong(); } public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool, @@ -1610,6 +1620,7 @@ public class BatteryStatsImpl extends BatteryStats { public void writeToParcel(Parcel out, long elapsedRealtimeUs) { super.writeToParcel(out, elapsedRealtimeUs); out.writeLong(getMaxDurationMsLocked(elapsedRealtimeUs / 1000)); + out.writeLong(getTotalDurationMsLocked(elapsedRealtimeUs / 1000)); } /** @@ -1617,12 +1628,13 @@ public class BatteryStatsImpl extends BatteryStats { * * Since the time base is probably meaningless after we come back, reading * from this will have the effect of stopping the timer. So here all we write - * is the max duration. + * is the max and total durations. */ @Override public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) { super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); out.writeLong(getMaxDurationMsLocked(elapsedRealtimeUs / 1000)); + out.writeLong(getTotalDurationMsLocked(elapsedRealtimeUs / 1000)); } /** @@ -1634,6 +1646,7 @@ public class BatteryStatsImpl extends BatteryStats { public void readSummaryFromParcelLocked(Parcel in) { super.readSummaryFromParcelLocked(in); mMaxDurationMs = in.readLong(); + mTotalDurationMs = in.readLong(); mStartTimeMs = -1; mCurrentDurationMs = 0; } @@ -1689,6 +1702,7 @@ public class BatteryStatsImpl extends BatteryStats { public void stopRunningLocked(long elapsedRealtimeMs) { if (mNesting == 1) { final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs); + mTotalDurationMs += durationMs; if (durationMs > mMaxDurationMs) { mMaxDurationMs = durationMs; } @@ -1704,6 +1718,7 @@ public class BatteryStatsImpl extends BatteryStats { public boolean reset(boolean detachIfReset) { boolean result = super.reset(detachIfReset); mMaxDurationMs = 0; + mTotalDurationMs = 0; mCurrentDurationMs = 0; if (mNesting > 0) { mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000; @@ -1732,6 +1747,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * Returns the time since the timer was started. + * Returns 0 if the timer is not currently running. * * Note that this time is NOT split between the timers in the timer group that * this timer is attached to. It is the TOTAL time. @@ -1745,6 +1761,20 @@ public class BatteryStatsImpl extends BatteryStats { } return durationMs; } + + /** + * Returns the total cumulative duration that this timer has been on since reset(). + * If mTimerPool == null, this should be the same + * as getTotalTimeLocked(elapsedRealtimeMs*1000, STATS_SINCE_CHARGED)/1000. + * + * Note that this time is NOT split between the timers in the timer group that + * this timer is attached to. It is the TOTAL time. For this reason, if mTimerPool != null, + * the result will not be equivalent to getTotalTimeLocked. + */ + @Override + public long getTotalDurationMsLocked(long elapsedRealtimeMs) { + return mTotalDurationMs + getCurrentDurationMsLocked(elapsedRealtimeMs); + } } /** @@ -1969,6 +1999,116 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * State for keeping track of two DurationTimers with different TimeBases, presumably where one + * TimeBase is effectively a subset of the other. + */ + public static class DualTimer { + // mMainTimer typically tracks the total time. May be pooled (but since it's a durationTimer, + // it also has the unpooled getTotalDurationMsLocked() for STATS_SINCE_CHARGED). + private final DurationTimer mMainTimer; + // mSubTimer typically tracks only part of the total time, such as background time, as + // determined by a subTimeBase. It is NOT pooled. + private final DurationTimer mSubTimer; + + /** + * Creates a DualTimer to hold a mMainTimer and a mSubTimer. + * The mMainTimer is based on the given timeBase and timerPool. + * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if + * the mMainTimer is. + */ + public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool, + TimeBase timeBase, TimeBase subTimeBase, Parcel in) { + mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase, in); + mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase, in); + } + + /** + * Creates a DualTimer to hold a mMainTimer and a mSubTimer. + * The mMainTimer is based on the given timeBase and timerPool. + * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if + * the mMainTimer is. + */ + public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool, + TimeBase timeBase, TimeBase subTimeBase) { + mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase); + mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase); + } + + /** Get the main timer. */ + public DurationTimer getMainTimer() { + return mMainTimer; + } + + /** Get the secondary timer. */ + public DurationTimer getSubTimer() { + return mSubTimer; + } + + public void startRunningLocked(long elapsedRealtimeMs) { + mMainTimer.startRunningLocked(elapsedRealtimeMs); + mSubTimer.startRunningLocked(elapsedRealtimeMs); + } + + public void stopRunningLocked(long elapsedRealtimeMs) { + mMainTimer.stopRunningLocked(elapsedRealtimeMs); + mSubTimer.stopRunningLocked(elapsedRealtimeMs); + } + + public void stopAllRunningLocked(long elapsedRealtimeMs) { + mMainTimer.stopAllRunningLocked(elapsedRealtimeMs); + mSubTimer.stopAllRunningLocked(elapsedRealtimeMs); + } + + public void setMark(long elapsedRealtimeMs) { + mMainTimer.setMark(elapsedRealtimeMs); + mSubTimer.setMark(elapsedRealtimeMs); + } + + public boolean reset(boolean detachIfReset) { + boolean active = false; + active |= !mMainTimer.reset(detachIfReset); + active |= !mSubTimer.reset(detachIfReset); + return !active; + } + + public void detach() { + mMainTimer.detach(); + mSubTimer.detach(); + } + + /** + * Writes a possibly null DualTimer to a Parcel. + * + * @param out the Parcel to which to write. + * @param t a DualTimer, or null. + */ + public static void writeDualTimerToParcel(Parcel out, DualTimer t, long elapsedRealtimeUs) { + if (t != null) { + out.writeInt(1); + t.writeToParcel(out, elapsedRealtimeUs); + } else { + out.writeInt(0); + } + } + + public void writeToParcel(Parcel out, long elapsedRealtimeUs) { + mMainTimer.writeToParcel(out, elapsedRealtimeUs); + mSubTimer.writeToParcel(out, elapsedRealtimeUs); + } + + public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) { + mMainTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); + mSubTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); + } + + public void readSummaryFromParcelLocked(Parcel in) { + mMainTimer.readSummaryFromParcelLocked(in); + mSubTimer.readSummaryFromParcelLocked(in); + } + } + + public abstract class OverflowArrayMap<T> { private static final String OVERFLOW_NAME = "*overflow*"; @@ -3146,7 +3286,13 @@ public class BatteryStatsImpl extends BatteryStats { public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime, long realtime) { - mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime); + boolean batteryStatusChanged = mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime); + + if (batteryStatusChanged) { + for (int i=0; i<mUidStats.size(); i++) { + mUidStats.valueAt(i).updateBgTimeBase(uptime, realtime); + } + } boolean unpluggedScreenOff = unplugged && screenOff; if (unpluggedScreenOff != mOnBatteryScreenOffTimeBase.isRunning()) { @@ -4485,8 +4631,8 @@ public class BatteryStatsImpl extends BatteryStats { private void noteBluetoothScanStartedLocked(int uid) { uid = mapUid(uid); - final long elapsedRealtime = SystemClock.elapsedRealtime(); - final long uptime = SystemClock.uptimeMillis(); + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); if (mBluetoothScanNesting == 0) { mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: " @@ -4507,8 +4653,8 @@ public class BatteryStatsImpl extends BatteryStats { private void noteBluetoothScanStoppedLocked(int uid) { uid = mapUid(uid); - final long elapsedRealtime = SystemClock.elapsedRealtime(); - final long uptime = SystemClock.uptimeMillis(); + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); mBluetoothScanNesting--; if (mBluetoothScanNesting == 0) { mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; @@ -4529,8 +4675,8 @@ public class BatteryStatsImpl extends BatteryStats { public void noteResetBluetoothScanLocked() { if (mBluetoothScanNesting > 0) { - final long elapsedRealtime = SystemClock.elapsedRealtime(); - final long uptime = SystemClock.uptimeMillis(); + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); mBluetoothScanNesting = 0; mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "BLE can stopped for: " @@ -5204,6 +5350,13 @@ public class BatteryStatsImpl extends BatteryStats { return true; } + private static boolean resetTimerIfNotNull(DualTimer timer, boolean detachIfReset) { + if (timer != null) { + return timer.reset(detachIfReset); + } + return true; + } + private static void detachLongCounterIfNotNull(LongSamplingCounter counter) { if (counter != null) { counter.detach(); @@ -5228,6 +5381,10 @@ public class BatteryStatsImpl extends BatteryStats { final int mUid; + /** TimeBase for when uid is in background and device is on battery. */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public final TimeBase mOnBatteryBackgroundTimeBase; + boolean mWifiRunning; StopwatchTimer mWifiRunningTimer; @@ -5235,7 +5392,7 @@ public class BatteryStatsImpl extends BatteryStats { StopwatchTimer mFullWifiLockTimer; boolean mWifiScanStarted; - StopwatchTimer mWifiScanTimer; + DualTimer mWifiScanTimer; static final int NO_BATCHED_SCAN_STARTED = -1; int mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED; @@ -5249,7 +5406,7 @@ public class BatteryStatsImpl extends BatteryStats { StopwatchTimer mFlashlightTurnedOnTimer; StopwatchTimer mCameraTurnedOnTimer; StopwatchTimer mForegroundActivityTimer; - StopwatchTimer mBluetoothScanTimer; + DualTimer mBluetoothScanTimer; int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; StopwatchTimer[] mProcessStateTimer; @@ -5343,6 +5500,10 @@ public class BatteryStatsImpl extends BatteryStats { mBsi = bsi; mUid = uid; + mOnBatteryBackgroundTimeBase = new TimeBase(); + mOnBatteryBackgroundTimeBase.init(mBsi.mClocks.uptimeMillis() * 1000, + mBsi.mClocks.elapsedRealtime() * 1000); + mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); mCpuPower = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); @@ -5369,8 +5530,8 @@ public class BatteryStatsImpl extends BatteryStats { mBsi.mWifiRunningTimers, mBsi.mOnBatteryTimeBase); mFullWifiLockTimer = new StopwatchTimer(mBsi.mClocks, this, FULL_WIFI_LOCK, mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase); - mWifiScanTimer = new StopwatchTimer(mBsi.mClocks, this, WIFI_SCAN, - mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase); + mWifiScanTimer = new DualTimer(mBsi.mClocks, this, WIFI_SCAN, + mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase); mWifiBatchedScanTimer = new StopwatchTimer[NUM_WIFI_BATCHED_SCAN_BINS]; mWifiMulticastTimer = new StopwatchTimer(mBsi.mClocks, this, WIFI_MULTICAST_ENABLED, mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase); @@ -5457,8 +5618,9 @@ public class BatteryStatsImpl extends BatteryStats { if (!mWifiScanStarted) { mWifiScanStarted = true; if (mWifiScanTimer == null) { - mWifiScanTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, WIFI_SCAN, - mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase); + mWifiScanTimer = new DualTimer(mBsi.mClocks, Uid.this, WIFI_SCAN, + mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, + mOnBatteryBackgroundTimeBase); } mWifiScanTimer.startRunningLocked(elapsedRealtimeMs); } @@ -5665,10 +5827,11 @@ public class BatteryStatsImpl extends BatteryStats { return mForegroundActivityTimer; } - public StopwatchTimer createBluetoothScanTimerLocked() { + public DualTimer createBluetoothScanTimerLocked() { if (mBluetoothScanTimer == null) { - mBluetoothScanTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, BLUETOOTH_SCAN_ON, - mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase); + mBluetoothScanTimer = new DualTimer(mBsi.mClocks, Uid.this, BLUETOOTH_SCAN_ON, + mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase, + mOnBatteryBackgroundTimeBase); } return mBluetoothScanTimer; } @@ -5741,7 +5904,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mWifiScanTimer == null) { return 0; } - return mWifiScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + return mWifiScanTimer.getMainTimer().getTotalTimeLocked(elapsedRealtimeUs, which); } @Override @@ -5749,7 +5912,33 @@ public class BatteryStatsImpl extends BatteryStats { if (mWifiScanTimer == null) { return 0; } - return mWifiScanTimer.getCountLocked(which); + return mWifiScanTimer.getMainTimer().getCountLocked(which); + } + + @Override + public int getWifiScanBackgroundCount(int which) { + if (mWifiScanTimer == null) { + return 0; + } + return mWifiScanTimer.getSubTimer().getCountLocked(which); + } + + @Override + public long getWifiScanActualTime(final long elapsedRealtimeUs) { + if (mWifiScanTimer == null) { + return 0; + } + final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000; + return mWifiScanTimer.getMainTimer().getTotalDurationMsLocked(elapsedRealtimeMs) * 1000; + } + + @Override + public long getWifiScanBackgroundTime(final long elapsedRealtimeUs) { + if (mWifiScanTimer == null) { + return 0; + } + final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000; + return mWifiScanTimer.getSubTimer().getTotalDurationMsLocked(elapsedRealtimeMs) * 1000; } @Override @@ -5805,7 +5994,18 @@ public class BatteryStatsImpl extends BatteryStats { @Override public Timer getBluetoothScanTimer() { - return mBluetoothScanTimer; + if (mBluetoothScanTimer == null) { + return null; + } + return mBluetoothScanTimer.getMainTimer(); + } + + @Override + public Timer getBluetoothScanBackgroundTimer() { + if (mBluetoothScanTimer == null) { + return null; + } + return mBluetoothScanTimer.getSubTimer(); } void makeProcessState(int i, Parcel in) { @@ -6202,6 +6402,9 @@ public class BatteryStatsImpl extends BatteryStats { mLastStepUserTime = mLastStepSystemTime = 0; mCurStepUserTime = mCurStepSystemTime = 0; + mOnBatteryBackgroundTimeBase.reset(mBsi.mClocks.elapsedRealtime() * 1000, + mBsi.mClocks.uptimeMillis() * 1000); + if (!active) { if (mWifiRunningTimer != null) { mWifiRunningTimer.detach(); @@ -6293,7 +6496,9 @@ public class BatteryStatsImpl extends BatteryStats { return !active; } - void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) { + void writeToParcelLocked(Parcel out, long uptimeUs, long elapsedRealtimeUs) { + mOnBatteryBackgroundTimeBase.writeToParcel(out, uptimeUs, elapsedRealtimeUs); + final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap(); int NW = wakeStats.size(); out.writeInt(NW); @@ -6511,6 +6716,8 @@ public class BatteryStatsImpl extends BatteryStats { } void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) { + mOnBatteryBackgroundTimeBase.readFromParcel(in); + int numWakelocks = in.readInt(); mWakelockStats.clear(); for (int j = 0; j < numWakelocks; j++) { @@ -6545,7 +6752,8 @@ public class BatteryStatsImpl extends BatteryStats { for (int k = 0; k < numSensors; k++) { int sensorNumber = in.readInt(); Uid.Sensor sensor = new Sensor(mBsi, this, sensorNumber); - sensor.readFromParcelLocked(mBsi.mOnBatteryTimeBase, in); + sensor.readFromParcelLocked(mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, + in); mSensorStats.put(sensorNumber, sensor); } @@ -6583,8 +6791,9 @@ public class BatteryStatsImpl extends BatteryStats { } mWifiScanStarted = false; if (in.readInt() != 0) { - mWifiScanTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, WIFI_SCAN, - mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, in); + mWifiScanTimer = new DualTimer(mBsi.mClocks, Uid.this, WIFI_SCAN, + mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, + in); } else { mWifiScanTimer = null; } @@ -6634,8 +6843,9 @@ public class BatteryStatsImpl extends BatteryStats { mForegroundActivityTimer = null; } if (in.readInt() != 0) { - mBluetoothScanTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, BLUETOOTH_SCAN_ON, - mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase, in); + mBluetoothScanTimer = new DualTimer(mBsi.mClocks, Uid.this, BLUETOOTH_SCAN_ON, + mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase, + mOnBatteryBackgroundTimeBase, in); } else { mBluetoothScanTimer = null; } @@ -6932,14 +7142,12 @@ public class BatteryStatsImpl extends BatteryStats { protected BatteryStatsImpl mBsi; /** - * BatteryStatsImpl that we are associated with. + * Uid that we are associated with. */ protected Uid mUid; final int mHandle; - StopwatchTimer mTimer; - - Counter mBgCounter; + DualTimer mTimer; public Sensor(BatteryStatsImpl bsi, Uid uid, int handle) { mBsi = bsi; @@ -6947,7 +7155,8 @@ public class BatteryStatsImpl extends BatteryStats { mHandle = handle; } - private StopwatchTimer readTimerFromParcel(TimeBase timeBase, Parcel in) { + private DualTimer readTimersFromParcel( + TimeBase timeBase, TimeBase bgTimeBase, Parcel in) { if (in.readInt() == 0) { return null; } @@ -6957,23 +7166,10 @@ public class BatteryStatsImpl extends BatteryStats { pool = new ArrayList<StopwatchTimer>(); mBsi.mSensorTimers.put(mHandle, pool); } - return new StopwatchTimer(mBsi.mClocks, mUid, 0, pool, timeBase, in); - } - - private Counter readCounterFromParcel(TimeBase timeBase, Parcel in) { - if (in.readInt() == 0) { - return null; - } - return new Counter(timeBase, in); + return new DualTimer(mBsi.mClocks, mUid, 0, pool, timeBase, bgTimeBase, in); } boolean reset() { - if (mBgCounter != null) { - mBgCounter.reset(true /*detachIfReset*/); - // If we detach, we must null the mBgCounter reference so that it - // can be recreated and attached. - mBgCounter = null; - } if (mTimer.reset(true)) { mTimer = null; return true; @@ -6981,24 +7177,28 @@ public class BatteryStatsImpl extends BatteryStats { return false; } - void readFromParcelLocked(TimeBase timeBase, Parcel in) { - mTimer = readTimerFromParcel(timeBase, in); - mBgCounter = readCounterFromParcel(timeBase, in); + void readFromParcelLocked(TimeBase timeBase, TimeBase bgTimeBase, Parcel in) { + mTimer = readTimersFromParcel(timeBase, bgTimeBase, in); } void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) { - Timer.writeTimerToParcel(out, mTimer, elapsedRealtimeUs); - Counter.writeCounterToParcel(out, mBgCounter); + DualTimer.writeDualTimerToParcel(out, mTimer, elapsedRealtimeUs); } @Override public Timer getSensorTime() { - return mTimer; + if (mTimer == null) { + return null; + } + return mTimer.getMainTimer(); } @Override - public Counter getSensorBgCount() { - return mBgCounter; + public Timer getSensorBackgroundTime() { + if (mTimer == null) { + return null; + } + return mTimer.getSubTimer(); } @Override @@ -7735,18 +7935,29 @@ public class BatteryStatsImpl extends BatteryStats { if (mProcessState == uidRunningState) return; - final long elapsedRealtime = mBsi.mClocks.elapsedRealtime(); + final long elapsedRealtimeMs = mBsi.mClocks.elapsedRealtime(); + final long uptimeMs = mBsi.mClocks.uptimeMillis(); if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) { - mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtime); + mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs); } mProcessState = uidRunningState; if (uidRunningState != ActivityManager.PROCESS_STATE_NONEXISTENT) { if (mProcessStateTimer[uidRunningState] == null) { makeProcessState(uidRunningState, null); } - mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtime); + mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs); } + + updateBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000); + } + + public boolean updateBgTimeBase(long uptimeUs, long realtimeUs) { + // Note that PROCESS_STATE_CACHED and ActivityManager.PROCESS_STATE_NONEXISTENT is + // also considered to be 'background' for our purposes, because it's not foreground. + boolean isBgAndUnplugged = mBsi.mOnBatteryTimeBase.isRunning() + && mProcessState >= PROCESS_STATE_BACKGROUND; + return mOnBatteryBackgroundTimeBase.setRunning(isBgAndUnplugged, uptimeUs, realtimeUs); } public SparseArray<? extends Pid> getPidStats() { @@ -7820,7 +8031,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - public StopwatchTimer getSensorTimerLocked(int sensor, boolean create) { + public DualTimer getSensorTimerLocked(int sensor, boolean create) { Sensor se = mSensorStats.get(sensor); if (se == null) { if (!create) { @@ -7829,7 +8040,7 @@ public class BatteryStatsImpl extends BatteryStats { se = new Sensor(mBsi, this, sensor); mSensorStats.put(sensor, se); } - StopwatchTimer t = se.mTimer; + DualTimer t = se.mTimer; if (t != null) { return t; } @@ -7838,28 +8049,12 @@ public class BatteryStatsImpl extends BatteryStats { timers = new ArrayList<StopwatchTimer>(); mBsi.mSensorTimers.put(sensor, timers); } - t = new StopwatchTimer(mBsi.mClocks, this, BatteryStats.SENSOR, timers, - mBsi.mOnBatteryTimeBase); + t = new DualTimer(mBsi.mClocks, this, BatteryStats.SENSOR, timers, + mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase); se.mTimer = t; return t; } - public Counter getSensorBgCounterLocked(int sensor, boolean create) { - Sensor se = mSensorStats.get(sensor); - if (se == null) { - if (!create) { - return null; - } - se = new Sensor(mBsi, this, sensor); - mSensorStats.put(sensor, se); - } - Counter c = se.mBgCounter; - if (c != null) return c; - c = new Counter(mBsi.mOnBatteryTimeBase); - se.mBgCounter = c; - return c; - } - public void noteStartSyncLocked(String name, long elapsedRealtimeMs) { StopwatchTimer t = mSyncStats.startObject(name); if (t != null) { @@ -7932,39 +8127,24 @@ public class BatteryStatsImpl extends BatteryStats { } public void noteStartSensor(int sensor, long elapsedRealtimeMs) { - StopwatchTimer t = getSensorTimerLocked(sensor, /* create= */ true); + DualTimer t = getSensorTimerLocked(sensor, /* create= */ true); t.startRunningLocked(elapsedRealtimeMs); - - Counter c = getSensorBgCounterLocked(sensor, /* create= */ true); - if (mProcessState >= PROCESS_STATE_BACKGROUND && t.mNesting == 1) { - c.stepAtomic(); - } } public void noteStopSensor(int sensor, long elapsedRealtimeMs) { // Don't create a timer if one doesn't already exist - StopwatchTimer t = getSensorTimerLocked(sensor, false); + DualTimer t = getSensorTimerLocked(sensor, false); if (t != null) { t.stopRunningLocked(elapsedRealtimeMs); } } public void noteStartGps(long elapsedRealtimeMs) { - StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, /* create= */ true); - t.startRunningLocked(elapsedRealtimeMs); - - Counter c = getSensorBgCounterLocked(Sensor.GPS, /* create= */ true); - if (mProcessState >= PROCESS_STATE_BACKGROUND && t.mNesting == 1) { - c.stepAtomic(); - } + noteStartSensor(Sensor.GPS, elapsedRealtimeMs); } public void noteStopGps(long elapsedRealtimeMs) { - // Don't create a timer if one doesn't already exist - StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, false); - if (t != null) { - t.stopRunningLocked(elapsedRealtimeMs); - } + noteStopSensor(Sensor.GPS, elapsedRealtimeMs); } public BatteryStatsImpl getBatteryStats() { @@ -8955,7 +9135,7 @@ public class BatteryStatsImpl extends BatteryStats { final Uid uid = mUidStats.valueAt(i); // Sum the total scan power for all apps. - totalScanTimeMs += uid.mWifiScanTimer.getTimeSinceMarkLocked( + totalScanTimeMs += uid.mWifiScanTimer.getMainTimer().getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; // Sum the total time holding wifi lock for all apps. @@ -8976,7 +9156,7 @@ public class BatteryStatsImpl extends BatteryStats { for (int i = 0; i < uidStatsSize; i++) { final Uid uid = mUidStats.valueAt(i); - long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked( + long scanTimeSinceMarkMs = uid.mWifiScanTimer.getMainTimer().getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; if (scanTimeSinceMarkMs > 0) { // Set the new mark so that next time we get new data since this point. @@ -9230,7 +9410,7 @@ public class BatteryStatsImpl extends BatteryStats { mHasBluetoothReporting = true; - final long elapsedRealtimeMs = SystemClock.elapsedRealtime(); + final long elapsedRealtimeMs = mClocks.elapsedRealtime(); final long rxTimeMs = info.getControllerRxTimeMillis(); final long txTimeMs = info.getControllerTxTimeMillis(); @@ -9250,7 +9430,7 @@ public class BatteryStatsImpl extends BatteryStats { continue; } - totalScanTimeMs += u.mBluetoothScanTimer.getTimeSinceMarkLocked( + totalScanTimeMs += u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; } @@ -9271,7 +9451,7 @@ public class BatteryStatsImpl extends BatteryStats { continue; } - long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getTimeSinceMarkLocked( + long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; if (scanTimeSinceMarkMs > 0) { // Set the new mark so that next time we get new data since this point. @@ -10777,6 +10957,8 @@ public class BatteryStatsImpl extends BatteryStats { Uid u = new Uid(this, uid); mUidStats.put(uid, u); + u.mOnBatteryBackgroundTimeBase.readSummaryFromParcel(in); + u.mWifiRunning = false; if (in.readInt() != 0) { u.mWifiRunningTimer.readSummaryFromParcelLocked(in); @@ -10934,8 +11116,7 @@ public class BatteryStatsImpl extends BatteryStats { for (int is = 0; is < NP; is++) { int seNumber = in.readInt(); if (in.readInt() != 0) { - u.getSensorTimerLocked(seNumber, true) - .readSummaryFromParcelLocked(in); + u.getSensorTimerLocked(seNumber, true).readSummaryFromParcelLocked(in); } } @@ -11141,6 +11322,8 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(mUidStats.keyAt(iu)); Uid u = mUidStats.valueAt(iu); + u.mOnBatteryBackgroundTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS); + if (u.mWifiRunningTimer != null) { out.writeInt(1); u.mWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); @@ -11724,7 +11907,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(mUidStats.keyAt(i)); Uid uid = mUidStats.valueAt(i); - uid.writeToParcelLocked(out, uSecRealtime); + uid.writeToParcelLocked(out, uSecUptime, uSecRealtime); } } else { out.writeInt(0); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java new file mode 100644 index 000000000000..6b52b98e4758 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2016 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 android.os.BatteryStats.STATS_SINCE_CHARGED; + +import android.app.ActivityManager; +import android.os.BatteryStats; +import android.os.WorkSource; +import android.support.test.filters.SmallTest; + +import junit.framework.TestCase; + +/** + * Test BatteryStatsImpl onBatteryBackgroundTimeBase TimeBase. + */ +public class BatteryStatsBackgroundStatsTest extends TestCase { + + private static final int UID = 10500; + + /** Test that BatteryStatsImpl.Uid.mOnBatteryBackgroundTimeBase works correctly. */ + @SmallTest + public void testBgTimeBase() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + long cur = 0; // realtime in us + + BatteryStatsImpl.TimeBase bgtb = bi.getOnBatteryBackgroundTimeBase(UID); + + // Off-battery, non-existent + clocks.realtime = clocks.uptime = 10; + cur = clocks.realtime * 1000; + bi.updateTimeBasesLocked(false, false, cur, cur); // off battery + assertFalse(bgtb.isRunning()); + assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + + // Off-battery, foreground + clocks.realtime = clocks.uptime = 100; + cur = clocks.realtime * 1000; + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + assertFalse(bgtb.isRunning()); + assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + + // Off-battery, background + clocks.realtime = clocks.uptime = 201; + cur = clocks.realtime * 1000; + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + assertFalse(bgtb.isRunning()); + assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + + // On-battery, background + clocks.realtime = clocks.uptime = 303; + cur = clocks.realtime * 1000; + bi.updateTimeBasesLocked(true, false, cur, cur); // on battery + // still in ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + assertTrue(bgtb.isRunning()); + assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + + // On-battery, background - but change screen state + clocks.realtime = clocks.uptime = 409; + cur = clocks.realtime * 1000; + bi.updateTimeBasesLocked(true, true, cur, cur); // on battery (again) + assertTrue(bgtb.isRunning()); + assertEquals(106_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + + // On-battery, background - but a different background state + clocks.realtime = clocks.uptime = 417; + cur = clocks.realtime * 1000; + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_RECEIVER); // background too + assertTrue(bgtb.isRunning()); + assertEquals(114_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + + // Off-battery, foreground + clocks.realtime = clocks.uptime = 530; + cur = clocks.realtime * 1000; + bi.updateTimeBasesLocked(false, false, cur, cur); // off battery + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + assertFalse(bgtb.isRunning()); + assertEquals(227_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + + // Off-battery, non-existent + clocks.realtime = clocks.uptime = 690; + cur = clocks.realtime * 1000; + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_NONEXISTENT); + assertFalse(bgtb.isRunning()); + assertEquals(227_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED)); + } + + @SmallTest + public void testWifiScan() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + long curr = 0; // realtime in us + + // On battery + curr = 1000 * (clocks.realtime = clocks.uptime = 100); + bi.updateTimeBasesLocked(true, false, curr, curr); // on battery + // App in foreground + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + + // Start timer + curr = 1000 * (clocks.realtime = clocks.uptime = 202); + bi.noteWifiScanStartedLocked(UID); + + // Move to background + curr = 1000 * (clocks.realtime = clocks.uptime = 254); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + + // Off battery + curr = 1000 * (clocks.realtime = clocks.uptime = 305); + bi.updateTimeBasesLocked(false, false, curr, curr); // off battery + + // Stop timer + curr = 1000 * (clocks.realtime = clocks.uptime = 409); + bi.noteWifiScanStoppedLocked(UID); + + // Test + curr = 1000 * (clocks.realtime = clocks.uptime = 657); + long time = bi.getUidStats().get(UID).getWifiScanTime(curr, STATS_SINCE_CHARGED); + int count = bi.getUidStats().get(UID).getWifiScanCount(STATS_SINCE_CHARGED); + int bgCount = bi.getUidStats().get(UID).getWifiScanBackgroundCount(STATS_SINCE_CHARGED); + long actualTime = bi.getUidStats().get(UID).getWifiScanActualTime(curr); + long bgTime = bi.getUidStats().get(UID).getWifiScanBackgroundTime(curr); + assertEquals((305 - 202) * 1000, time); + assertEquals(1, count); + assertEquals(1, bgCount); + assertEquals((305 - 202) * 1000, actualTime); + assertEquals((305 - 254) * 1000, bgTime); + } + + @SmallTest + public void testAppBluetoothScan() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + WorkSource ws = new WorkSource(UID); // needed for bluetooth + long curr = 0; // realtime in us + + // On battery + curr = 1000 * (clocks.realtime = clocks.uptime = 100); + bi.updateTimeBasesLocked(true, false, curr, curr); // on battery + + // App in foreground + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + + // Start timer + curr = 1000 * (clocks.realtime = clocks.uptime = 202); + bi.noteBluetoothScanStartedFromSourceLocked(ws); + + // Move to background + curr = 1000 * (clocks.realtime = clocks.uptime = 254); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + + // Off battery + curr = 1000 * (clocks.realtime = clocks.uptime = 305); + bi.updateTimeBasesLocked(false, false, curr, curr); // off battery + + // Stop timer + curr = 1000 * (clocks.realtime = clocks.uptime = 409); + bi.noteBluetoothScanStoppedFromSourceLocked(ws); + + // Test + curr = 1000 * (clocks.realtime = clocks.uptime = 657); + BatteryStats.Timer timer = bi.getUidStats().get(UID).getBluetoothScanTimer(); + BatteryStats.Timer bgTimer = bi.getUidStats().get(UID).getBluetoothScanBackgroundTimer(); + + long time = timer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED); + int count = timer.getCountLocked(STATS_SINCE_CHARGED); + int bgCount = bgTimer.getCountLocked(STATS_SINCE_CHARGED); + long actualTime = timer.getTotalDurationMsLocked(clocks.realtime) * 1000; + long bgTime = bgTimer.getTotalDurationMsLocked(clocks.realtime) * 1000; + assertEquals((305 - 202) * 1000, time); + assertEquals(1, count); + assertEquals(1, bgCount); + assertEquals((305 - 202) * 1000, actualTime); + assertEquals((305 - 254) * 1000, bgTime); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java index f1aeecc46265..a1b05cdbd9d8 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java @@ -44,18 +44,21 @@ public class BatteryStatsDurationTimerTest extends TestCase { assertFalse(timer.isRunningLocked()); assertEquals(0, timer.getCurrentDurationMsLocked(300)); assertEquals(0, timer.getMaxDurationMsLocked(301)); + assertEquals(0, timer.getTotalDurationMsLocked(301)); - // Start timer: current and max advance + // Start timer: current, total, and max advance timer.startRunningLocked(700); assertTrue(timer.isRunningLocked()); assertEquals(800, timer.getCurrentDurationMsLocked(1500)); assertEquals(801, timer.getMaxDurationMsLocked(1501)); + assertEquals(802, timer.getTotalDurationMsLocked(1502)); - // Stop timer: current resets to 0, max remains + // Stop timer: current resets to 0; total and max remain timer.stopRunningLocked(3100); assertFalse(timer.isRunningLocked()); assertEquals(0, timer.getCurrentDurationMsLocked(6300)); assertEquals(2400, timer.getMaxDurationMsLocked(6301)); + assertEquals(2400, timer.getTotalDurationMsLocked(6302)); // Start time again, but check with a short time, and make sure max doesn't // increment. @@ -63,31 +66,36 @@ public class BatteryStatsDurationTimerTest extends TestCase { assertTrue(timer.isRunningLocked()); assertEquals(100, timer.getCurrentDurationMsLocked(12800)); assertEquals(2400, timer.getMaxDurationMsLocked(12801)); + assertEquals(2502, timer.getTotalDurationMsLocked(12802)); // And stop it again, but with a short time, and make sure it doesn't increment. timer.stopRunningLocked(12900); assertFalse(timer.isRunningLocked()); assertEquals(0, timer.getCurrentDurationMsLocked(13000)); assertEquals(2400, timer.getMaxDurationMsLocked(13001)); + assertEquals(2600, timer.getTotalDurationMsLocked(13002)); // Now start and check that the time doesn't increase if the two times are the same. timer.startRunningLocked(27000); assertTrue(timer.isRunningLocked()); assertEquals(0, timer.getCurrentDurationMsLocked(27000)); assertEquals(2400, timer.getMaxDurationMsLocked(27000)); + assertEquals(2600, timer.getTotalDurationMsLocked(27000)); // Stop the TimeBase. The values should be frozen. timeBase.setRunning(false, /* uptimeUs */ 10, /* realtimeUs */ 55000*1000); assertTrue(timer.isRunningLocked()); assertEquals(28000, timer.getCurrentDurationMsLocked(110100)); assertEquals(28000, timer.getMaxDurationMsLocked(110101)); + assertEquals(30600, timer.getTotalDurationMsLocked(110102)); // Start the TimeBase. The values should be the old value plus the delta - // between when the timer restarted and the current time + // between when the timer restarted and the current time. timeBase.setRunning(true, /* uptimeUs */ 10, /* realtimeUs */ 220100*1000); assertTrue(timer.isRunningLocked()); assertEquals(28200, timer.getCurrentDurationMsLocked(220300)); assertEquals(28201, timer.getMaxDurationMsLocked(220301)); + assertEquals(30802, timer.getTotalDurationMsLocked(220302)); } @SmallTest @@ -114,6 +122,7 @@ public class BatteryStatsDurationTimerTest extends TestCase { // Check that it did start running assertEquals(400, timer.getMaxDurationMsLocked(700)); assertEquals(401, timer.getCurrentDurationMsLocked(701)); + assertEquals(402, timer.getTotalDurationMsLocked(702)); // Write summary final Parcel summaryParcel = Parcel.obtain(); @@ -128,8 +137,9 @@ public class BatteryStatsDurationTimerTest extends TestCase { // The new one shouldn't be running, and therefore 0 for current time assertFalse(summary.isRunningLocked()); assertEquals(0, summary.getCurrentDurationMsLocked(6300)); - // The new one should have the max duration that we had when we wrote it + // The new one should have the max and total durations that we had when we wrote it assertEquals(1200, summary.getMaxDurationMsLocked(6301)); + assertEquals(1200, summary.getTotalDurationMsLocked(6302)); // Write full final Parcel fullParcel = Parcel.obtain(); @@ -142,7 +152,9 @@ public class BatteryStatsDurationTimerTest extends TestCase { // The new one shouldn't be running, and therefore 0 for current time assertFalse(full.isRunningLocked()); assertEquals(0, full.getCurrentDurationMsLocked(6300)); - // The new one should have the max duration that we had when we wrote it + // The new one should have the max and total durations that we had when we wrote it assertEquals(1200, full.getMaxDurationMsLocked(6301)); + assertEquals(1200, full.getTotalDurationMsLocked(6302)); + } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java index b4afddab97c9..251ceb04b973 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java @@ -146,6 +146,8 @@ public class BatteryStatsSamplingTimerTest extends TestCase { BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase); // Start running on battery. + // (Note that the wrong units are used in this class. setRunning is actually supposed to + // take us, not the ms that clocks uses.) timeBase.setRunning(true, clocks.uptimeMillis(), clocks.elapsedRealtime()); // The first update on battery consumes the values as a way of starting cleanly. diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java index 4ec78ff5be7f..a41a0235bef4 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java @@ -17,9 +17,7 @@ package com.android.internal.os; import android.app.ActivityManager; import android.os.BatteryStats; -import android.os.Debug; import android.support.test.filters.SmallTest; -import android.util.Log; import junit.framework.TestCase; @@ -31,6 +29,9 @@ public class BatteryStatsSensorTest extends TestCase { private static final int UID = 10500; private static final int SENSOR_ID = -10000; + // TODO: fix the bug in StopwatchTimer to prevent this bug from manifesting here. + boolean revealCntBug = false; + @SmallTest public void testSensorStartStop() throws Exception { final MockClocks clocks = new MockClocks(); @@ -38,7 +39,7 @@ public class BatteryStatsSensorTest extends TestCase { bi.mForceOnBattery = true; clocks.realtime = 100; clocks.uptime = 100; - bi.getOnBatteryTimeBase().setRunning(true, 100, 100); + bi.getOnBatteryTimeBase().setRunning(true, 100_000, 100_000); bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY); bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); bi.noteStartSensorLocked(UID, SENSOR_ID); @@ -56,58 +57,345 @@ public class BatteryStatsSensorTest extends TestCase { BatteryStats.Timer sensorTimer = bi.getUidStats().get(UID).getSensorStats() .get(SENSOR_ID).getSensorTime(); - BatteryStats.Counter sensorBgCounter = bi.getUidStats().get(UID).getSensorStats() - .get(SENSOR_ID).getSensorBgCount(); + BatteryStats.Timer sensorBgTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorBackgroundTime(); assertEquals(2, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); - assertEquals(300000, - sensorTimer.getTotalTimeLocked(clocks.realtime, BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(300_000, sensorTimer.getTotalTimeLocked( + clocks.realtime * 1000, BatteryStats.STATS_SINCE_CHARGED)); + + assertEquals(1, sensorBgTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(200_000, sensorBgTimer.getTotalTimeLocked( + clocks.realtime * 1000, BatteryStats.STATS_SINCE_CHARGED)); + } + + @SmallTest + public void testCountingWhileOffBattery() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + long curr = 0; // realtime in us + + // Plugged-in (battery=off, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 100); + bi.updateTimeBasesLocked(false, false, curr, curr); + + + // Start sensor (battery=off, sensor=on) + curr = 1000 * (clocks.realtime = clocks.uptime = 200); + bi.noteStartSensorLocked(UID, SENSOR_ID); + + // Test situation + curr = 1000 * (clocks.realtime = clocks.uptime = 215); + BatteryStats.Timer sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + assertEquals(0, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + if(revealCntBug) { + assertEquals(0, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } else { + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } + + // Stop sensor (battery=off, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 550); + bi.noteStopSensorLocked(UID, SENSOR_ID); + + // Test situation + curr = 1000 * (clocks.realtime = clocks.uptime = 678); + sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + assertEquals(0, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(0, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } + + @SmallTest + public void testCountingWhileOnBattery() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + long curr = 0; // realtime in us + + // Unplugged (battery=on, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 100); + bi.updateTimeBasesLocked(true, false, curr, curr); + + // Start sensor (battery=on, sensor=on) + curr = 1000 * (clocks.realtime = clocks.uptime = 200); + bi.noteStartSensorLocked(UID, SENSOR_ID); + + // Test situation + curr = 1000 * (clocks.realtime = clocks.uptime = 215); + BatteryStats.Timer sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + assertEquals((215-200)*1000, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + + // Stop sensor (battery=on, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 550); + bi.noteStopSensorLocked(UID, SENSOR_ID); + + // Test situation + curr = 1000 * (clocks.realtime = clocks.uptime = 678); + sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + assertEquals((550-200)*1000, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } + + @SmallTest + public void testBatteryStatusOnToOff() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + long curr = 0; // realtime in us + + // On battery (battery=on, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 100); + bi.updateTimeBasesLocked(true, false, curr, curr); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + + // Start sensor (battery=on, sensor=on) + curr = 1000 * (clocks.realtime = clocks.uptime = 202); + bi.noteStartSensorLocked(UID, SENSOR_ID); + + // Off battery (battery=off, sensor=on) + curr = 1000 * (clocks.realtime = clocks.uptime = 305); + bi.updateTimeBasesLocked(false, false, curr, curr); + + // Stop sensor while off battery (battery=off, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 409); + bi.noteStopSensorLocked(UID, SENSOR_ID); + + // Start sensor while off battery (battery=off, sensor=on) + curr = 1000 * (clocks.realtime = clocks.uptime = 519); + bi.noteStartSensorLocked(UID, SENSOR_ID); + + // Test while still running (but off battery) + curr = 1000 * (clocks.realtime = clocks.uptime = 657); + BatteryStats.Timer sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + if(revealCntBug) { + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } else { + assertEquals(2, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } + assertEquals((305-202)*1000, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + + // Now stop running (still off battery) (battery=off, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 693); + bi.noteStopSensorLocked(UID, SENSOR_ID); + + sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + assertEquals((305-202)*1000, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + } + + @SmallTest + public void testBatteryStatusOffToOn() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + long curr = 0; // realtime in us + + // Plugged-in (battery=off, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 100); + bi.updateTimeBasesLocked(false, false, curr, curr); + + // Start sensor (battery=off, sensor=on) + curr = 1000 * (clocks.realtime = clocks.uptime = 200); + bi.noteStartSensorLocked(UID, SENSOR_ID); + + // Test situation + curr = 1000 * (clocks.realtime = clocks.uptime = 215); + BatteryStats.Timer sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + // Time was entirely off battery, so time=0. + assertEquals(0, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + // Acquired off battery, so count=0. + if(revealCntBug) { + assertEquals(0, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } else { + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } + + // Unplug (battery=on, sensor=on) + curr = 1000 * (clocks.realtime = clocks.uptime = 305); + bi.updateTimeBasesLocked(true, false, curr, curr); + + //Test situation + curr = 1000 * (clocks.realtime = clocks.uptime = 410); + sensorTimer = bi.getUidStats().get(UID).getSensorStats().get(SENSOR_ID).getSensorTime(); + // Part of the time it was on battery. + assertEquals((410-305)*1000, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + // Only ever acquired off battery, so count=0. + if(revealCntBug) { + assertEquals(0, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } else { + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } + + // Stop sensor (battery=on, sensor=off) + curr = 1000 * (clocks.realtime = clocks.uptime = 550); + bi.noteStopSensorLocked(UID, SENSOR_ID); - assertEquals(1, sensorBgCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + // Test situation + curr = 1000 * (clocks.realtime = clocks.uptime = 678); + sensorTimer = bi.getUidStats().get(UID).getSensorStats().get(SENSOR_ID).getSensorTime(); + // Part of the time it was on battery. + assertEquals((550-305)*1000, + sensorTimer.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + // Only ever acquired off battery, so count=0. + if(revealCntBug) { + assertEquals(0, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } else { + assertEquals(1, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } } @SmallTest - public void testNestedSensorReset() throws Exception { + public void testPooledBackgroundUsage() throws Exception { + final int UID_2 = 20000; // second uid for testing pool usage final MockClocks clocks = new MockClocks(); MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); bi.mForceOnBattery = true; - clocks.realtime = 100; - clocks.uptime = 100; - bi.getOnBatteryTimeBase().setRunning(true, 100, 100); - bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_RECEIVER); + long curr = 0; // realtime in us + // Entire test is on-battery + curr = 1000 * (clocks.realtime = clocks.uptime = 1000); + bi.updateTimeBasesLocked(true, false, curr, curr); - clocks.realtime += 100; - clocks.uptime += 100; + // See below for a diagram of events. + // UID in foreground + curr = 1000 * (clocks.realtime = clocks.uptime = 2002); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + + // UID starts the sensor (foreground) + curr = 1000 * (clocks.realtime = clocks.uptime = 3004); bi.noteStartSensorLocked(UID, SENSOR_ID); - clocks.realtime += 100; - clocks.uptime += 100; + // UID_2 in background + curr = 1000 * (clocks.realtime = clocks.uptime = 4008); + bi.noteUidProcessStateLocked(UID_2, ActivityManager.PROCESS_STATE_RECEIVER); // background + + // UID_2 starts the sensor (background) + curr = 1000 * (clocks.realtime = clocks.uptime = 5016); + bi.noteStartSensorLocked(UID_2, SENSOR_ID); - // The sensor is started and the background counter has been created. - final BatteryStats.Uid uid = bi.getUidStats().get(UID); - assertNotNull(uid); + // UID enters background + curr = 1000 * (clocks.realtime = clocks.uptime = 6032); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); - BatteryStats.Uid.Sensor sensor = uid.getSensorStats().get(SENSOR_ID); - assertNotNull(sensor); - assertNotNull(sensor.getSensorTime()); - assertNotNull(sensor.getSensorBgCount()); + // UID enters background again (from a different background state) + curr = 1000 * (clocks.realtime = clocks.uptime = 7004); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_LAST_ACTIVITY); - // Reset the stats. Since the sensor is still running, we should still see the sensor - // timer. Background counter should be gone though. - bi.getUidStatsLocked(UID).reset(); + // UID_2 stops the sensor (background), then starts it again, then stops again + curr = 1000 * (clocks.realtime = clocks.uptime = 8064); + bi.noteStopSensorLocked(UID_2, SENSOR_ID); + curr = 1000 * (clocks.realtime = clocks.uptime = 9128); + bi.noteStartSensorLocked(UID_2, SENSOR_ID); + curr = 1000 * (clocks.realtime = clocks.uptime = 10256); + bi.noteStopSensorLocked(UID_2, SENSOR_ID); - sensor = uid.getSensorStats().get(SENSOR_ID); - assertNotNull(sensor); - assertNotNull(sensor.getSensorTime()); - assertNull(sensor.getSensorBgCount()); + // UID re-enters foreground + curr = 1000 * (clocks.realtime = clocks.uptime = 11512); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + + // UID starts the sensor a second time (foreground) + curr = 1000 * (clocks.realtime = clocks.uptime = 12000); + bi.noteStartSensorLocked(UID, SENSOR_ID); + // UID re-enters background + curr = 1000 * (clocks.realtime = clocks.uptime = 13002); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + + // UID stops the sensor completely (background) + curr = 1000 * (clocks.realtime = clocks.uptime = 14004); + bi.noteStopSensorLocked(UID, SENSOR_ID); + curr = 1000 * (clocks.realtime = clocks.uptime = 14024); bi.noteStopSensorLocked(UID, SENSOR_ID); - // Now the sensor timer has stopped so this reset should also take out the sensor. - bi.getUidStatsLocked(UID).reset(); + // UID starts the sensor anew (background) + curr = 1000 * (clocks.realtime = clocks.uptime = 15010); + bi.noteStartSensorLocked(UID, SENSOR_ID); + + // UID stops the sensor (background) + curr = 1000 * (clocks.realtime = clocks.uptime = 16020); + bi.noteStopSensorLocked(UID, SENSOR_ID); + +// Summary +// UID +// foreground: 2002---6032, 11512---13002 +// background: 6032---------------11512, 13002-------------------------- +// sensor running: 3004-----------------------------14024, 15010-16020 +// +// UID2 +// foreground: +// background: 4008------------------------------------------------------- +// sensor running: 5016--8064, 9128-10256 + + BatteryStats.Timer timer1 = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + BatteryStats.Timer bgTimer1 = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorBackgroundTime(); + + BatteryStats.Timer timer2 = bi.getUidStats().get(UID_2).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + BatteryStats.Timer bgTimer2 = bi.getUidStats().get(UID_2).getSensorStats() + .get(SENSOR_ID).getSensorBackgroundTime(); + + // Expected values + long expActualTime1 = (14024 - 3004) + (16020 - 15010); + long expBgTime1 = (11512 - 6032) + (14024 - 13002) + (16020 - 15010); + + long expActualTime2 = (8064 - 5016) + (10256 - 9128); + long expBgTime2 = (8064 - 5016) + (10256 - 9128); + + long expBlamedTime1 = (5016 - 3004) + (8064 - 5016)/2 + (9128 - 8064) + (10256 - 9128)/2 + + (14024 - 10256) + (16020 - 15010); + long expBlamedTime2 = (8064 - 5016)/2 + (10256 - 9128)/2; + + // Test: UID - blamed time + assertEquals(expBlamedTime1 * 1000, + timer1.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + // Test: UID - actual time + assertEquals(expActualTime1 * 1000, + timer1.getTotalDurationMsLocked(clocks.realtime) * 1000 ); + // Test: UID - background time + // bg timer ignores pools, so both totalTime and totalDuration should give the same result + assertEquals(expBgTime1 * 1000, + bgTimer1.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(expBgTime1 * 1000, + bgTimer1.getTotalDurationMsLocked(clocks.realtime) * 1000 ); + // Test: UID - count + assertEquals(2, timer1.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + // Test: UID - background count + if(revealCntBug) { + assertEquals(1, bgTimer1.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } else { + assertEquals(2, bgTimer1.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } - sensor = uid.getSensorStats().get(SENSOR_ID); - assertNull(sensor); + // Test: UID_2 - blamed time + assertEquals(expBlamedTime2 * 1000, + timer2.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + // Test: UID_2 - actual time + assertEquals(expActualTime2 * 1000, + timer2.getTotalDurationMsLocked(clocks.realtime) * 1000); + // Test: UID_2 - background time + // bg timer ignores pools, so both totalTime and totalDuration should give the same result + assertEquals(expBgTime2 * 1000, + bgTimer2.getTotalTimeLocked(curr, BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(expBgTime2 * 1000, + bgTimer2.getTotalDurationMsLocked(clocks.realtime) * 1000 ); + // Test: UID_2 - count + assertEquals(2, timer2.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + // Test: UID_2 - background count + assertEquals(2, bgTimer2.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); } } 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 c7cd0ee710e1..11132683d4d1 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -12,6 +12,7 @@ import org.junit.runners.Suite; BatteryStatsTimerTest.class, BatteryStatsUidTest.class, BatteryStatsSensorTest.class, + BatteryStatsBackgroundStatsTest.class, }) public class BatteryStatsTests { } 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 10541060398a..65f898c62815 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -26,6 +26,7 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { MockBatteryStatsImpl(Clocks clocks) { super(clocks); this.clocks = mClocks; + mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase); } MockBatteryStatsImpl() { @@ -39,5 +40,9 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { public boolean isOnBattery() { return mForceOnBattery ? true : super.isOnBattery(); } + + public TimeBase getOnBatteryBackgroundTimeBase(int uid) { + return getUidStatsLocked(uid).mOnBatteryBackgroundTimeBase; + } } |