diff options
| author | 2016-08-08 19:58:55 +0000 | |
|---|---|---|
| committer | 2016-08-08 19:58:57 +0000 | |
| commit | 154cc8237b2cae4f88293076886fd0a22f8f947d (patch) | |
| tree | ff19dca2625615181d11010682f1079f58bc6b22 | |
| parent | 5a3e33a82a36d22a39f0efd743b259256c9fad54 (diff) | |
| parent | adbee556a9e8f399acda17958eed1ba33ad07218 (diff) | |
Merge "Have BatteryStats track and report the running wakelocks." into nyc-mr1-dev
4 files changed, 451 insertions, 24 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index f0cc3905e991..a0c2efd407ba 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -30,6 +30,8 @@ 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; import android.util.Printer; @@ -47,6 +49,7 @@ import com.android.internal.os.BatteryStatsHelper; * @hide */ public abstract class BatteryStats implements Parcelable { + private static final String TAG = "BatteryStats"; private static final boolean LOCAL_LOGV = false; @@ -175,8 +178,11 @@ public abstract class BatteryStats implements Parcelable { /** * Current version of checkin data format. + * + * New in version 19: + * - Wakelock data (wl) gets current and max times. */ - static final String CHECKIN_VERSION = "18"; + static final String CHECKIN_VERSION = "19"; /** * Old version, we hit 9 and ran out of room, need to remove. @@ -352,6 +358,32 @@ public abstract class BatteryStats implements Parcelable { public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs); /** + * Returns the max duration if it is being tracked. + * Not all Timer subclasses track the max duration and the current duration. + + */ + public long getMaxDurationMsLocked(long elapsedRealtimeMs) { + return -1; + } + + /** + * 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. + */ + public long getCurrentDurationMsLocked(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. + */ + public boolean isRunningLocked() { + return false; + } + + /** * Temporary for debugging. */ public abstract void logState(Printer pw, String prefix); @@ -2558,6 +2590,22 @@ public abstract class BatteryStats implements Parcelable { sb.append('('); sb.append(count); sb.append(" times)"); + final long maxDurationMs = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000); + if (maxDurationMs >= 0) { + sb.append(" max="); + sb.append(maxDurationMs); + } + if (timer.isRunningLocked()) { + final long currentMs = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000); + if (currentMs >= 0) { + sb.append(" (running for "); + sb.append(currentMs); + sb.append("ms)"); + } else { + sb.append(" (running)"); + } + } + return ", "; } } @@ -2565,6 +2613,7 @@ public abstract class BatteryStats implements Parcelable { } /** + * Prints details about a timer, if its total time was greater than 0. * * @param pw a PrintWriter object to print to. * @param sb a StringBuilder object. @@ -2573,24 +2622,40 @@ public abstract class BatteryStats implements Parcelable { * @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. + * @return true if anything was printed. */ private static final boolean printTimer(PrintWriter pw, StringBuilder sb, Timer timer, - long rawRealtime, int which, String prefix, String type) { + long rawRealtimeUs, int which, String prefix, String type) { if (timer != null) { // Convert from microseconds to milliseconds with rounding - final long totalTime = (timer.getTotalTimeLocked( - rawRealtime, which) + 500) / 1000; + final long totalTimeMs = (timer.getTotalTimeLocked( + rawRealtimeUs, which) + 500) / 1000; final int count = timer.getCountLocked(which); - if (totalTime != 0) { + if (totalTimeMs != 0) { sb.setLength(0); sb.append(prefix); sb.append(" "); sb.append(type); sb.append(": "); - formatTimeMs(sb, totalTime); + formatTimeMs(sb, totalTimeMs); sb.append("realtime ("); sb.append(count); sb.append(" times)"); + final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs/1000); + if (maxDurationMs >= 0) { + sb.append(" max="); + sb.append(maxDurationMs); + } + if (timer.isRunningLocked()) { + final long currentMs = timer.getCurrentDurationMsLocked(rawRealtimeUs/1000); + if (currentMs >= 0) { + sb.append(" (running for "); + sb.append(currentMs); + sb.append("ms)"); + } else { + sb.append(" (running)"); + } + } pw.println(sb.toString()); return true; } @@ -2613,15 +2678,23 @@ public abstract class BatteryStats implements Parcelable { long elapsedRealtimeUs, String name, int which, String linePrefix) { long totalTimeMicros = 0; int count = 0; + long max = -1; + long current = -1; if (timer != null) { totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which); count = timer.getCountLocked(which); + current = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000); + max = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000); } sb.append(linePrefix); sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding sb.append(','); sb.append(name != null ? name + "," : ""); sb.append(count); + sb.append(','); + sb.append(current); + sb.append(','); + sb.append(max); return ","; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 17635aefe6ac..7fb92aef30bd 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -108,7 +108,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 148 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 149 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -1566,6 +1566,186 @@ public class BatteryStatsImpl extends BatteryStats { } } + + /** + * A StopwatchTimer that also tracks the total and max individual + * time spent active according to the given timebase. Whereas + * StopwatchTimer apportions the time amongst all in the pool, + * the total and max durations are not apportioned. + */ + public static class DurationTimer extends StopwatchTimer { + /** + * The time (in ms) that the timer was last acquired or the time base + * last (re-)started. Increasing the nesting depth does not reset this time. + * + * -1 if the timer is currently not running or the time base is not running. + * + * If written to a parcel, the start time is reset, as is mNesting in the base class + * StopwatchTimer. + */ + long mStartTimeMs = -1; + + /** + * The longest time period (in ms) that the timer has been active. + */ + long mMaxDurationMs; + + /** + * The total time (in ms) that that the timer has been active since reset(). + */ + long mCurrentDurationMs; + + 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(); + } + + public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool, + TimeBase timeBase) { + super(clocks, uid, type, timerPool, timeBase); + } + + @Override + public void writeToParcel(Parcel out, long elapsedRealtimeUs) { + super.writeToParcel(out, elapsedRealtimeUs); + out.writeLong(mMaxDurationMs); + } + + /** + * Write the summary to the parcel. + * + * 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. + */ + @Override + public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) { + super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); + out.writeLong(mMaxDurationMs); + } + + /** + * Read the summary parcel. + * + * Has the side effect of stopping the timer. + */ + @Override + public void readSummaryFromParcelLocked(Parcel in) { + super.readSummaryFromParcelLocked(in); + mMaxDurationMs = in.readLong(); + mStartTimeMs = -1; + mCurrentDurationMs = 0; + } + + /** + * The TimeBase time started (again). + * + * If the timer is also running, store the start time. + */ + public void onTimeStarted(long elapsedRealtimeUs, long baseUptime, long baseRealtime) { + super.onTimeStarted(elapsedRealtimeUs, baseUptime, baseRealtime); + if (mNesting > 0) { + mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000; + } + } + + /** + * The TimeBase stopped running. + * + * If the timer is running, add the duration into mCurrentDurationMs. + */ + @Override + public void onTimeStopped(long elapsedRealtimeUs, long baseUptime, long baseRealtime) { + super.onTimeStopped(elapsedRealtimeUs, baseUptime, baseRealtime); + if (mNesting > 0) { + mCurrentDurationMs += (elapsedRealtimeUs / 1000) - mStartTimeMs; + } + mStartTimeMs = -1; + } + + @Override + public void logState(Printer pw, String prefix) { + super.logState(pw, prefix); + } + + @Override + public void startRunningLocked(long elapsedRealtimeMs) { + super.startRunningLocked(elapsedRealtimeMs); + if (mNesting == 1 && mTimeBase.isRunning()) { + // Just started + mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000; + } + } + + /** + * Decrements the mNesting ref-count on this timer. + * + * If it actually stopped (mNesting went to 0), then possibly update + * mMaxDuration if the current duration was the longest ever. + */ + @Override + public void stopRunningLocked(long elapsedRealtimeMs) { + super.stopRunningLocked(elapsedRealtimeMs); + if (mNesting == 0) { + final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs); + if (durationMs > mMaxDurationMs) { + mMaxDurationMs = durationMs; + } + mStartTimeMs = -1; + mCurrentDurationMs = 0; + } + } + + @Override + public boolean reset(boolean detachIfReset) { + boolean result = super.reset(detachIfReset); + mMaxDurationMs = 0; + mCurrentDurationMs = 0; + if (mNesting > 0) { + mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000; + } else { + mStartTimeMs = -1; + } + return result; + } + + /** + * Returns the max duration that this timer has ever seen. + * + * 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. + */ + @Override + public long getMaxDurationMsLocked(long elapsedRealtimeMs) { + if (mNesting > 0) { + final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs); + if (durationMs > mMaxDurationMs) { + return durationMs; + } + } + return mMaxDurationMs; + } + + /** + * Returns the time since the timer was started. + * + * 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. + */ + @Override + public long getCurrentDurationMsLocked(long elapsedRealtimeMs) { + long durationMs = mCurrentDurationMs; + if (mNesting > 0) { + if (mTimeBase.isRunning()) { + durationMs += (mTimeBase.getRealtime(elapsedRealtimeMs*1000)/1000) + - mStartTimeMs; + } + } + return durationMs; + } + } + /** * State for keeping track of timing information. */ @@ -6535,7 +6715,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * How long (in ms) this uid has been keeping the device partially awake. */ - StopwatchTimer mTimerPartial; + DurationTimer mTimerPartial; /** * How long (in ms) this uid has been keeping the device fully awake. @@ -6564,8 +6744,8 @@ public class BatteryStatsImpl extends BatteryStats { * @param in the Parcel to be read from. * return a new Timer, or null. */ - private StopwatchTimer readTimerFromParcel(int type, ArrayList<StopwatchTimer> pool, - TimeBase timeBase, Parcel in) { + private StopwatchTimer readStopwatchTimerFromParcel(int type, + ArrayList<StopwatchTimer> pool, TimeBase timeBase, Parcel in) { if (in.readInt() == 0) { return null; } @@ -6573,6 +6753,22 @@ public class BatteryStatsImpl extends BatteryStats { return new StopwatchTimer(mBsi.mClocks, mUid, type, pool, timeBase, in); } + /** + * Reads a possibly null Timer from a Parcel. The timer is associated with the + * proper timer pool from the given BatteryStatsImpl object. + * + * @param in the Parcel to be read from. + * return a new Timer, or null. + */ + private DurationTimer readDurationTimerFromParcel(int type, + ArrayList<StopwatchTimer> pool, TimeBase timeBase, Parcel in) { + if (in.readInt() == 0) { + return null; + } + + return new DurationTimer(mBsi.mClocks, mUid, type, pool, timeBase, in); + } + boolean reset() { boolean wlactive = false; if (mTimerFull != null) { @@ -6609,11 +6805,14 @@ public class BatteryStatsImpl extends BatteryStats { } void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) { - mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL, + mTimerPartial = readDurationTimerFromParcel(WAKE_TYPE_PARTIAL, mBsi.mPartialTimers, screenOffTimeBase, in); - mTimerFull = readTimerFromParcel(WAKE_TYPE_FULL, mBsi.mFullTimers, timeBase, in); - mTimerWindow = readTimerFromParcel(WAKE_TYPE_WINDOW, mBsi.mWindowTimers, timeBase, in); - mTimerDraw = readTimerFromParcel(WAKE_TYPE_DRAW, mBsi.mDrawTimers, timeBase, in); + mTimerFull = readStopwatchTimerFromParcel(WAKE_TYPE_FULL, + mBsi.mFullTimers, timeBase, in); + mTimerWindow = readStopwatchTimerFromParcel(WAKE_TYPE_WINDOW, + mBsi.mWindowTimers, timeBase, in); + mTimerDraw = readStopwatchTimerFromParcel(WAKE_TYPE_DRAW, + mBsi.mDrawTimers, timeBase, in); } void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) { @@ -6635,40 +6834,43 @@ public class BatteryStatsImpl extends BatteryStats { } public StopwatchTimer getStopwatchTimer(int type) { - StopwatchTimer t; switch (type) { - case WAKE_TYPE_PARTIAL: - t = mTimerPartial; + case WAKE_TYPE_PARTIAL: { + DurationTimer t = mTimerPartial; if (t == null) { - t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL, + t = new DurationTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL, mBsi.mPartialTimers, mBsi.mOnBatteryScreenOffTimeBase); mTimerPartial = t; } return t; - case WAKE_TYPE_FULL: - t = mTimerFull; + } + case WAKE_TYPE_FULL: { + StopwatchTimer t = mTimerFull; if (t == null) { t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_FULL, mBsi.mFullTimers, mBsi.mOnBatteryTimeBase); mTimerFull = t; } return t; - case WAKE_TYPE_WINDOW: - t = mTimerWindow; + } + case WAKE_TYPE_WINDOW: { + StopwatchTimer t = mTimerWindow; if (t == null) { t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_WINDOW, mBsi.mWindowTimers, mBsi.mOnBatteryTimeBase); mTimerWindow = t; } return t; - case WAKE_TYPE_DRAW: - t = mTimerDraw; + } + case WAKE_TYPE_DRAW: { + StopwatchTimer t = mTimerDraw; if (t == null) { t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_DRAW, mBsi.mDrawTimers, mBsi.mOnBatteryTimeBase); mTimerDraw = t; } return t; + } default: throw new IllegalArgumentException("type=" + type); } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java new file mode 100644 index 000000000000..a15e3679a181 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java @@ -0,0 +1,151 @@ +/* + * 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 android.os.BatteryStats; +import android.os.Parcel; +import android.support.test.filters.SmallTest; +import android.util.Log; + +import junit.framework.TestCase; + +import org.mockito.Mockito; + +/** + * Test BatteryStatsImpl.DurationTimer. + * + * In these tests, unless otherwise commented, the time increments by + * 2x + 100, to make the subtraction unlikely to alias to another time. + */ +public class BatteryStatsDurationTimerTest extends TestCase { + + @SmallTest + public void testStartStop() throws Exception { + final MockClocks clocks = new MockClocks(); + + final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase(); + timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime()); + + final BatteryStatsImpl.DurationTimer timer = new BatteryStatsImpl.DurationTimer(clocks, + null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase); + + // TimeBase running, timer not running: current and max are 0 + timeBase.setRunning(true, /* uptimeUs */ 0, /* realtimeUs */ 100*1000); + assertFalse(timer.isRunningLocked()); + assertEquals(0, timer.getCurrentDurationMsLocked(300)); + assertEquals(0, timer.getMaxDurationMsLocked(301)); + + // Start timer: current and max advance + timer.startRunningLocked(700); + assertTrue(timer.isRunningLocked()); + assertEquals(800, timer.getCurrentDurationMsLocked(1500)); + assertEquals(801, timer.getMaxDurationMsLocked(1501)); + + // Stop timer: current resets to 0, max remains + timer.stopRunningLocked(3100); + assertFalse(timer.isRunningLocked()); + assertEquals(0, timer.getCurrentDurationMsLocked(6300)); + assertEquals(2400, timer.getMaxDurationMsLocked(6301)); + + // Start time again, but check with a short time, and make sure max doesn't + // increment. + timer.startRunningLocked(12700); + assertTrue(timer.isRunningLocked()); + assertEquals(100, timer.getCurrentDurationMsLocked(12800)); + assertEquals(2400, timer.getMaxDurationMsLocked(12801)); + + // 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)); + + // 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)); + + // Stop the TimeBase. The values should be frozen. + timeBase.setRunning(false, /* uptimeUs */ 10, /* realtimeUs */ 55000*1000); + assertTrue(timer.isRunningLocked()); + assertEquals(28100, timer.getCurrentDurationMsLocked(110100)); // Why 28100 and not 28000? + assertEquals(28100, timer.getMaxDurationMsLocked(110101)); + + // Start the TimeBase. The values should be the old value plus the delta + // between when the timer restarted and the current time + timeBase.setRunning(true, /* uptimeUs */ 10, /* realtimeUs */ 220100*1000); + assertTrue(timer.isRunningLocked()); + assertEquals(28300, timer.getCurrentDurationMsLocked(220300)); // extra 100 from above?? + assertEquals(28301, timer.getMaxDurationMsLocked(220301)); + } + + @SmallTest + public void testReset() throws Exception { + } + + @SmallTest + public void testParceling() throws Exception { + final MockClocks clocks = new MockClocks(); + + final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase(); + timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime()); + + final BatteryStatsImpl.DurationTimer timer = new BatteryStatsImpl.DurationTimer(clocks, + null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase); + + // Start running on battery. + clocks.realtime = 100; + clocks.uptime = 10; + timeBase.setRunning(true, clocks.uptimeMillis()*1000, clocks.elapsedRealtime()*1000); + + timer.startRunningLocked(300); + + // Check that it did start running + assertEquals(400, timer.getMaxDurationMsLocked(700)); + assertEquals(401, timer.getCurrentDurationMsLocked(701)); + + // Write summary + final Parcel summaryParcel = Parcel.obtain(); + timer.writeSummaryFromParcelLocked(summaryParcel, 1500*1000); + summaryParcel.setDataPosition(0); + + // Read summary + final BatteryStatsImpl.DurationTimer summary = new BatteryStatsImpl.DurationTimer(clocks, + null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase); + summary.startRunningLocked(3100); + summary.readSummaryFromParcelLocked(summaryParcel); + // 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 + assertEquals(1200, summary.getMaxDurationMsLocked(6301)); + + // Write full + final Parcel fullParcel = Parcel.obtain(); + timer.writeToParcel(fullParcel, 1500*1000); + fullParcel.setDataPosition(0); + + // Read full - Should be the same as the summary as far as DurationTimer is concerned. + final BatteryStatsImpl.DurationTimer full = new BatteryStatsImpl.DurationTimer(clocks, + null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase, fullParcel); + // 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 + assertEquals(1200, full.getMaxDurationMsLocked(6301)); + } +} 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 78bcbbc4ecb9..9518219f8c6f 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -5,6 +5,7 @@ import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ + BatteryStatsDurationTimerTest.class, BatteryStatsSamplingTimerTest.class, BatteryStatsServTest.class, BatteryStatsTimeBaseTest.class, |