Merge "BatteryStats: Fix up semantics of SamplingTimer" into nyc-dev

This commit is contained in:
Adam Lesinski
2016-04-23 00:20:09 +00:00
committed by Android (Google) Code Review
2 changed files with 189 additions and 47 deletions

View File

@@ -1210,6 +1210,16 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
/**
* A counter meant to accept monotonically increasing values to its {@link #update(long, int)}
* method. The state of the timer according to its {@link TimeBase} will determine how much
* of the value is recorded.
*
* If the value being recorded resets, {@link #endSample()} can be called in order to
* account for the change. If the value passed in to {@link #update(long, int)} decreased
* between calls, the {@link #endSample()} is automatically called and the new value is
* expected to increase monotonically from that point on.
*/
public static class SamplingTimer extends Timer {
/**
@@ -1262,16 +1272,21 @@ public class BatteryStatsImpl extends BatteryStats {
}
@VisibleForTesting
public SamplingTimer(Clocks clocks, TimeBase timeBase, boolean trackReportedValues) {
public SamplingTimer(Clocks clocks, TimeBase timeBase) {
super(clocks, 0, timeBase);
mTrackingReportedValues = trackReportedValues;
mTrackingReportedValues = false;
mTimeBaseRunning = timeBase.isRunning();
}
public void setStale() {
mTrackingReportedValues = false;
mUnpluggedReportedTotalTime = 0;
mUnpluggedReportedCount = 0;
/**
* Ends the current sample, allowing subsequent values to {@link #update(long, int)} to
* be less than the values used for a previous invocation.
*/
public void endSample() {
mTotalTime = computeRunTimeLocked(0 /* unused by us */);
mCount = computeCurrentCountLocked();
mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = 0;
mUnpluggedReportedCount = mCurrentReportedCount = 0;
}
public void setUpdateVersion(int version) {
@@ -1282,34 +1297,46 @@ public class BatteryStatsImpl extends BatteryStats {
return mUpdateVersion;
}
public void updateCurrentReportedCount(int count) {
if (mTimeBaseRunning && mUnpluggedReportedCount == 0) {
/**
* Updates the current recorded values. These are meant to be monotonically increasing
* and cumulative. If you are dealing with deltas, use {@link #add(long, int)}.
*
* If the values being recorded have been reset, the monotonically increasing requirement
* will be broken. In this case, {@link #endSample()} is automatically called and
* the total value of totalTime and count are recorded, starting a new monotonically
* increasing sample.
*
* @param totalTime total time of sample in microseconds.
* @param count total number of times the event being sampled occurred.
*/
public void update(long totalTime, int count) {
if (mTimeBaseRunning && !mTrackingReportedValues) {
// Updating the reported value for the first time.
mUnpluggedReportedTotalTime = totalTime;
mUnpluggedReportedCount = count;
// If we are receiving an update update mTrackingReportedValues;
mTrackingReportedValues = true;
}
mTrackingReportedValues = true;
if (totalTime < mCurrentReportedTotalTime || count < mCurrentReportedCount) {
endSample();
}
mCurrentReportedTotalTime = totalTime;
mCurrentReportedCount = count;
}
public void addCurrentReportedCount(int delta) {
updateCurrentReportedCount(mCurrentReportedCount + delta);
}
public void updateCurrentReportedTotalTime(long totalTime) {
if (mTimeBaseRunning && mUnpluggedReportedTotalTime == 0) {
// Updating the reported value for the first time.
mUnpluggedReportedTotalTime = totalTime;
// If we are receiving an update update mTrackingReportedValues;
mTrackingReportedValues = true;
}
mCurrentReportedTotalTime = totalTime;
}
public void addCurrentReportedTotalTime(long delta) {
updateCurrentReportedTotalTime(mCurrentReportedTotalTime + delta);
/**
* Adds deltaTime and deltaCount to the current sample.
*
* @param deltaTime additional time recorded since the last sampled event, in microseconds.
* @param deltaCount additional number of times the event being sampled occurred.
*/
public void add(long deltaTime, int deltaCount) {
update(mCurrentReportedTotalTime + deltaTime, mCurrentReportedCount + deltaCount);
}
@Override
public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
super.onTimeStarted(elapsedRealtime, baseUptime, baseRealtime);
if (mTrackingReportedValues) {
@@ -1319,11 +1346,13 @@ public class BatteryStatsImpl extends BatteryStats {
mTimeBaseRunning = true;
}
@Override
public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
super.onTimeStopped(elapsedRealtime, baseUptime, baseRealtime);
mTimeBaseRunning = false;
}
@Override
public void logState(Printer pw, String prefix) {
super.logState(pw, prefix);
pw.println(prefix + "mCurrentReportedCount=" + mCurrentReportedCount
@@ -1332,16 +1361,19 @@ public class BatteryStatsImpl extends BatteryStats {
+ " mUnpluggedReportedTotalTime=" + mUnpluggedReportedTotalTime);
}
@Override
protected long computeRunTimeLocked(long curBatteryRealtime) {
return mTotalTime + (mTimeBaseRunning && mTrackingReportedValues
? mCurrentReportedTotalTime - mUnpluggedReportedTotalTime : 0);
}
@Override
protected int computeCurrentCountLocked() {
return mCount + (mTimeBaseRunning && mTrackingReportedValues
? mCurrentReportedCount - mUnpluggedReportedCount : 0);
}
@Override
public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
super.writeToParcel(out, elapsedRealtimeUs);
out.writeInt(mCurrentReportedCount);
@@ -1351,12 +1383,16 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mTrackingReportedValues ? 1 : 0);
}
@Override
public boolean reset(boolean detachIfReset) {
super.reset(detachIfReset);
setStale();
mTrackingReportedValues = false;
mUnpluggedReportedTotalTime = 0;
mUnpluggedReportedCount = 0;
return true;
}
@Override
public void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
super.writeSummaryFromParcelLocked(out, batteryRealtime);
out.writeLong(mCurrentReportedTotalTime);
@@ -1364,6 +1400,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mTrackingReportedValues ? 1 : 0);
}
@Override
public void readSummaryFromParcelLocked(Parcel in) {
super.readSummaryFromParcelLocked(in);
mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = in.readLong();
@@ -2002,7 +2039,7 @@ public class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getWakeupReasonTimerLocked(String name) {
SamplingTimer timer = mWakeupReasonStats.get(name);
if (timer == null) {
timer = new SamplingTimer(mClocks, mOnBatteryTimeBase, true);
timer = new SamplingTimer(mClocks, mOnBatteryTimeBase);
mWakeupReasonStats.put(name, timer);
}
return timer;
@@ -2015,8 +2052,7 @@ public class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getKernelWakelockTimerLocked(String name) {
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
kwlt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase,
true /* track reported values */);
kwlt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase);
mKernelWakelockStats.put(name, kwlt);
}
return kwlt;
@@ -3125,8 +3161,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mLastWakeupReason != null) {
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.addCurrentReportedCount(1);
timer.addCurrentReportedTotalTime(deltaUptime * 1000); // time is in microseconds
timer.add(deltaUptime * 1000, 1); // time in in microseconds
mLastWakeupReason = null;
}
}
@@ -8727,16 +8762,15 @@ public class BatteryStatsImpl extends BatteryStats {
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
kwlt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase,
true /* track reported val */);
kwlt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase);
mKernelWakelockStats.put(name, kwlt);
}
kwlt.updateCurrentReportedCount(kws.mCount);
kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
kwlt.update(kws.mTotalTime, kws.mCount);
kwlt.setUpdateVersion(kws.mVersion);
if (kws.mVersion != wakelockStats.kernelWakelockVersion)
seenNonZeroTime |= kws.mTotalTime > 0;
if (kws.mVersion != wakelockStats.kernelWakelockVersion) {
seenNonZeroTime |= kws.mTotalTime > 0;
}
}
int numWakelocksSetStale = 0;
@@ -8745,7 +8779,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
SamplingTimer st = ent.getValue();
if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) {
st.setStale();
st.endSample();
numWakelocksSetStale++;
}
}

View File

@@ -21,8 +21,119 @@ import android.support.test.filters.SmallTest;
import junit.framework.TestCase;
import org.mockito.Mockito;
public class BatteryStatsSamplingTimerTest extends TestCase {
@SmallTest
public void testSettingStalePreservesData() throws Exception {
final MockClocks clocks = new MockClocks();
final BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks,
Mockito.mock(BatteryStatsImpl.TimeBase.class));
timer.onTimeStarted(100, 100, 100);
// First update is absorbed.
timer.update(10, 1);
timer.update(20, 2);
assertEquals(1, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
assertEquals(10, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
timer.endSample();
assertEquals(1, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
assertEquals(10, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
timer.onTimeStopped(200, 200, 200);
assertEquals(1, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
assertEquals(10, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
}
@SmallTest
public void testEndSampleAndContinueWhenTimeOrCountDecreases() throws Exception {
final MockClocks clocks = new MockClocks();
final BatteryStatsImpl.TimeBase timeBase = Mockito.mock(BatteryStatsImpl.TimeBase.class);
final BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks,
timeBase);
// First once is absorbed.
timer.update(10, 1);
timer.add(10, 1);
assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
assertEquals(0, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
// This is less than we currently have, so we will end the sample. Time isn't running, so
// nothing should happen.
timer.update(0, 0);
assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
assertEquals(0, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
timer.onTimeStarted(100, 100, 100);
// This should add.
timer.add(100, 10);
assertEquals(100, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(10, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
// This is less than we currently have, so we should end our sample and continue with the
// entire amount updated here.
timer.update(50, 5);
assertEquals(150, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(15, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
timer.onTimeStopped(200, 200, 200);
assertEquals(150, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(15, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
}
@SmallTest
public void testFirstUpdateIsAbsorbed() throws Exception {
final MockClocks clocks = new MockClocks();
final BatteryStatsImpl.TimeBase timeBase = Mockito.mock(BatteryStatsImpl.TimeBase.class);
BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
// This should be absorbed because it is our first update and we don't know what
// was being counted before.
timer.update(10, 1);
assertEquals(0, timer.getTotalTimeLocked(10, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
timer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
timer.onTimeStarted(100, 100, 100);
// This should be absorbed.
timer.update(10, 1);
assertEquals(0, timer.getTotalTimeLocked(100, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
// This should NOT be aborbed, since we've already done that.
timer.add(10, 1);
assertEquals(10, timer.getTotalTimeLocked(100, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(1, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
timer.onTimeStopped(200, 200, 200);
timer.onTimeStarted(300, 300, 300);
// This should NOT be absorbed.
timer.add(10, 1);
assertEquals(20, timer.getTotalTimeLocked(300, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(2, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
}
@SmallTest
public void testSampleTimerSummaryParceling() throws Exception {
final MockClocks clocks = new MockClocks();
@@ -32,18 +143,15 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase,
true);
BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
// Start running on battery.
timeBase.setRunning(true, clocks.uptimeMillis(), clocks.elapsedRealtime());
// The first update on battery consumes the values as a way of starting cleanly.
timer.addCurrentReportedTotalTime(10);
timer.addCurrentReportedCount(1);
timer.add(10, 1);
timer.addCurrentReportedTotalTime(10);
timer.addCurrentReportedCount(1);
timer.add(10, 1);
clocks.realtime = 20;
clocks.uptime = 20;
@@ -72,14 +180,14 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
// Read the on battery summary from the parcel.
BatteryStatsImpl.SamplingTimer unparceledTimer = new BatteryStatsImpl.SamplingTimer(
clocks, timeBase, true);
clocks, timeBase);
unparceledTimer.readSummaryFromParcelLocked(onBatterySummaryParcel);
assertEquals(10, unparceledTimer.getTotalTimeLocked(0, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(1, unparceledTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
// Read the off battery summary from the parcel.
unparceledTimer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase, true);
unparceledTimer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
unparceledTimer.readSummaryFromParcelLocked(offBatterySummaryParcel);
assertEquals(10, unparceledTimer.getTotalTimeLocked(0, BatteryStats.STATS_SINCE_CHARGED));