diff options
| author | 2018-01-26 10:26:36 -0800 | |
|---|---|---|
| committer | 2018-01-29 11:09:14 -0800 | |
| commit | 076218bfc0094c17f95c2e8afa4d5b2480f03f73 (patch) | |
| tree | e66bf0248b8b5af1d35a33a047eeb3c3874fa841 | |
| parent | 84783905c03d368352fbd05dfb614251a72f1786 (diff) | |
Keep track of battery drain rate in various states...
... and print in dumpsys power.
Bug: 72229630
Test: manual test with dumpsys power, etc
Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
Change-Id: I43949129ff03c1e0b0fa3aa603e0678b728538ee
7 files changed, 668 insertions, 2 deletions
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java index f3a95b90d5c1..a86237dd271f 100644 --- a/core/java/android/os/BatteryManagerInternal.java +++ b/core/java/android/os/BatteryManagerInternal.java @@ -24,26 +24,63 @@ package android.os; public abstract class BatteryManagerInternal { /** * Returns true if the device is plugged into any of the specified plug types. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract boolean isPowered(int plugTypeSet); /** * Returns the current plug type. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract int getPlugType(); /** * Returns battery level as a percentage. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract int getBatteryLevel(); /** + * Instantaneous battery capacity in uA-h, as defined in the HealthInfo HAL struct. + * Please note apparently it could be bigger than {@link #getBatteryFullCharge}. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. + * + * @see android.hardware.health.V1_0.HealthInfo#batteryChargeCounter + */ + public abstract int getBatteryChargeCounter(); + + /** + * Battery charge value when it is considered to be "full" in uA-h , as defined in the + * HealthInfo HAL struct. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. + * + * @see android.hardware.health.V1_0.HealthInfo#batteryFullCharge + */ + public abstract int getBatteryFullCharge(); + + /** * Returns whether we currently consider the battery level to be low. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract boolean getBatteryLevelLow(); /** * Returns a non-zero value if an unsupported charger is attached. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract int getInvalidCharger(); } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index ba9883b4de59..8265262d3824 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -835,6 +835,9 @@ public final class BatteryService extends SystemService { case "level": mHealthInfo.batteryLevel = Integer.parseInt(value); break; + case "counter": + mHealthInfo.batteryChargeCounter = Integer.parseInt(value); + break; case "temp": mHealthInfo.batteryTemperature = Integer.parseInt(value); break; @@ -1164,6 +1167,20 @@ public final class BatteryService extends SystemService { } @Override + public int getBatteryChargeCounter() { + synchronized (mLock) { + return mHealthInfo.batteryChargeCounter; + } + } + + @Override + public int getBatteryFullCharge() { + synchronized (mLock) { + return mHealthInfo.batteryFullCharge; + } + } + + @Override public boolean getBatteryLevelLow() { synchronized (mLock) { return mBatteryLevelLow; diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 732ac66b41de..219facd0b002 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -34,6 +34,7 @@ option java_package com.android.server 2731 power_soft_sleep_requested (savedwaketimems|2) # Power save state has changed. See BatterySaverController.java for the details. 2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5) +27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6) # # Leave IDs through 2740 for more power logs (2730 used by battery_discharge above) diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java index a538967e501d..847c90a08c5c 100644 --- a/services/core/java/com/android/server/power/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java @@ -33,6 +33,7 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.power.batterysaver.CpuFrequencies; import java.io.PrintWriter; @@ -498,6 +499,8 @@ public class BatterySaverPolicy extends ContentObserver { pw.print(" Noninteractive File values:\n"); dumpMap(pw, " ", mFilesForNoninteractive); pw.println(); + pw.println(); + BatterySavingStats.getInstance().dump(pw, " "); } } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index d4627c2da07e..32f38b7cf772 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.hardware.power.V1_0.PowerHint; import android.net.Uri; +import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -50,6 +51,9 @@ import com.android.server.LocalServices; import com.android.server.power.BatterySaverPolicy; import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener; import com.android.server.power.PowerManagerService; +import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState; +import com.android.server.power.batterysaver.BatterySavingStats.DozeState; +import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState; import java.util.ArrayList; @@ -70,6 +74,8 @@ public class BatterySaverController implements BatterySaverPolicyListener { private final BatterySaverPolicy mBatterySaverPolicy; + private final BatterySavingStats mBatterySavingStats; + private static final String WARNING_LINK_URL = "http://goto.google.com/extreme-battery-saver"; @GuardedBy("mLock") @@ -78,6 +84,9 @@ public class BatterySaverController implements BatterySaverPolicyListener { @GuardedBy("mLock") private boolean mEnabled; + @GuardedBy("mLock") + private boolean mIsPluggedIn; + /** * Previously enabled or not; only for the event logging. Only use it from * {@link #handleBatterySaverStateChanged}. @@ -104,15 +113,28 @@ public class BatterySaverController implements BatterySaverPolicyListener { private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Slog.d(TAG, "onReceive: " + intent); + } switch (intent.getAction()) { case Intent.ACTION_SCREEN_ON: case Intent.ACTION_SCREEN_OFF: if (!isEnabled()) { + updateBatterySavingStats(); return; // No need to send it if not enabled. } // Don't send the broadcast, because we never did so in this case. mHandler.postStateChanged(/*sendBroadcast=*/ false); break; + case Intent.ACTION_BATTERY_CHANGED: + synchronized (mLock) { + mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); + } + // Fall-through. + case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: + case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED: + updateBatterySavingStats(); + break; } } }; @@ -126,6 +148,7 @@ public class BatterySaverController implements BatterySaverPolicyListener { mBatterySaverPolicy = policy; mBatterySaverPolicy.addListener(this); mFileUpdater = new FileUpdater(context); + mBatterySavingStats = BatterySavingStats.getInstance(); // Initialize plugins. final ArrayList<Plugin> plugins = new ArrayList<>(); @@ -149,6 +172,9 @@ public class BatterySaverController implements BatterySaverPolicyListener { public void systemReady() { final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); mContext.registerReceiver(mReceiver, filter); mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class) @@ -280,7 +306,6 @@ public class BatterySaverController implements BatterySaverPolicyListener { enabled = mEnabled; mIsInteractive = isInteractive; - if (enabled) { fileValues = mBatterySaverPolicy.getFileValues(isInteractive); } else { @@ -293,6 +318,8 @@ public class BatterySaverController implements BatterySaverPolicyListener { pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0); } + updateBatterySavingStats(); + if (ArrayUtils.isEmpty(fileValues)) { mFileUpdater.restoreDefault(); } else { @@ -332,7 +359,6 @@ public class BatterySaverController implements BatterySaverPolicyListener { mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.DEVICE_POWER); - for (LowPowerModeListener listener : listeners) { final PowerSaveState result = mBatterySaverPolicy.getBatterySaverPolicy( @@ -388,4 +414,28 @@ public class BatterySaverController implements BatterySaverPolicyListener { foregroundUser); } } + + private void updateBatterySavingStats() { + final PowerManager pm = getPowerManager(); + if (pm == null) { + Slog.wtf(TAG, "PowerManager not initialized"); + return; + } + final boolean isInteractive = pm.isInteractive(); + final int dozeMode = + pm.isDeviceIdleMode() ? DozeState.DEEP + : pm.isLightDeviceIdleMode() ? DozeState.LIGHT + : DozeState.NOT_DOZING; + + synchronized (mLock) { + if (mIsPluggedIn) { + mBatterySavingStats.startCharging(); + return; + } + mBatterySavingStats.transitionState( + mEnabled ? BatterySaverState.ON : BatterySaverState.OFF, + isInteractive ? InteractiveState.INTERACTIVE : InteractiveState.NON_INTERACTIVE, + dozeMode); + } + } } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java new file mode 100644 index 000000000000..df4f8eca5595 --- /dev/null +++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2018 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.server.power.batterysaver; + +import android.os.BatteryManagerInternal; +import android.os.SystemClock; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.EventLogTags; +import com.android.server.LocalServices; +import com.android.server.power.BatterySaverPolicy; + +import java.io.PrintWriter; + +/** + * This class keeps track of battery drain rate. + * + * Test: + atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java + */ +public class BatterySavingStats { + + private static final String TAG = "BatterySavingStats"; + + private static final boolean DEBUG = BatterySaverPolicy.DEBUG; + + private final Object mLock = new Object(); + + /** Whether battery saver is on or off. */ + interface BatterySaverState { + int OFF = 0; + int ON = 1; + + int SHIFT = 0; + int BITS = 1; + int MASK = (1 << BITS) - 1; + + static int fromIndex(int index) { + return (index >> SHIFT) & MASK; + } + } + + /** Whether the device is interactive (i.e. screen on) or not. */ + interface InteractiveState { + int NON_INTERACTIVE = 0; + int INTERACTIVE = 1; + + int SHIFT = BatterySaverState.SHIFT + BatterySaverState.BITS; + int BITS = 1; + int MASK = (1 << BITS) - 1; + + static int fromIndex(int index) { + return (index >> SHIFT) & MASK; + } + } + + /** Doze mode. */ + interface DozeState { + int NOT_DOZING = 0; + int LIGHT = 1; + int DEEP = 2; + + int SHIFT = InteractiveState.SHIFT + InteractiveState.BITS; + int BITS = 2; + int MASK = (1 << BITS) - 1; + + static int fromIndex(int index) { + return (index >> SHIFT) & MASK; + } + } + + /** + * Various stats in each state. + */ + static class Stat { + public long startTime; + public long endTime; + + public int startBatteryLevel; + public int endBatteryLevel; + + public long totalTimeMillis; + public int totalBatteryDrain; + + public long totalMinutes() { + return totalTimeMillis / 60_000; + } + + public double drainPerHour() { + if (totalTimeMillis == 0) { + return 0; + } + return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000)); + } + + @VisibleForTesting + String toStringForTest() { + return "{" + totalMinutes() + "m," + totalBatteryDrain + "," + + String.format("%.2f", drainPerHour()) + "}"; + } + } + + private static BatterySavingStats sInstance; + + private BatteryManagerInternal mBatteryManagerInternal; + + private static final int STATE_NOT_INITIALIZED = -1; + private static final int STATE_CHARGING = -2; + + /** + * Current state, one of STATE_* or values returned by {@link #statesToIndex}. + */ + @GuardedBy("mLock") + private int mCurrentState = STATE_NOT_INITIALIZED; + + /** + * Stats in each state. + */ + @VisibleForTesting + @GuardedBy("mLock") + final ArrayMap<Integer, Stat> mStats = new ArrayMap<>(); + + /** + * Don't call it directly -- use {@link #getInstance()}. Not private for testing. + */ + @VisibleForTesting + BatterySavingStats() { + mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); + } + + public static synchronized BatterySavingStats getInstance() { + if (sInstance == null) { + sInstance = new BatterySavingStats(); + } + return sInstance; + } + + private BatteryManagerInternal getBatteryManagerInternal() { + if (mBatteryManagerInternal == null) { + mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); + } + return mBatteryManagerInternal; + } + + /** + * Takes a state triplet and generates a state index. + */ + @VisibleForTesting + static int statesToIndex( + int batterySaverState, int interactiveState, int dozeState) { + int ret = batterySaverState & BatterySaverState.MASK; + ret |= (interactiveState & InteractiveState.MASK) << InteractiveState.SHIFT; + ret |= (dozeState & DozeState.MASK) << DozeState.SHIFT; + return ret; + } + + /** + * Takes a state index and returns a string for logging. + */ + @VisibleForTesting + static String stateToString(int state) { + switch (state) { + case STATE_NOT_INITIALIZED: + return "NotInitialized"; + case STATE_CHARGING: + return "Charging"; + } + return "BS=" + BatterySaverState.fromIndex(state) + + ",I=" + InteractiveState.fromIndex(state) + + ",D=" + DozeState.fromIndex(state); + } + + /** + * @return {@link Stat} fo a given state. + */ + @VisibleForTesting + Stat getStat(int stateIndex) { + synchronized (mLock) { + Stat stat = mStats.get(stateIndex); + if (stat == null) { + stat = new Stat(); + mStats.put(stateIndex, stat); + } + return stat; + } + } + + /** + * @return {@link Stat} fo a given state triplet. + */ + private Stat getStat(int batterySaverState, int interactiveState, int dozeState) { + return getStat(statesToIndex(batterySaverState, interactiveState, dozeState)); + } + + long injectCurrentTime() { + return SystemClock.elapsedRealtime(); + } + + int injectBatteryLevel() { + final BatteryManagerInternal bmi = getBatteryManagerInternal(); + if (bmi == null) { + Slog.wtf(TAG, "BatteryManagerInternal not initialized"); + return 0; + } + return bmi.getBatteryChargeCounter(); + } + + /** + * Called from the outside whenever any of the states changes, when the device is not plugged + * in. + */ + public void transitionState(int batterySaverState, int interactiveState, int dozeState) { + synchronized (mLock) { + + final int newState = statesToIndex( + batterySaverState, interactiveState, dozeState); + if (mCurrentState == newState) { + return; + } + + endLastStateLocked(); + startNewStateLocked(newState); + } + } + + /** + * Called from the outside when the device is plugged in. + */ + public void startCharging() { + synchronized (mLock) { + if (mCurrentState < 0) { + return; + } + + endLastStateLocked(); + startNewStateLocked(STATE_CHARGING); + } + } + + private void endLastStateLocked() { + if (mCurrentState < 0) { + return; + } + final Stat stat = getStat(mCurrentState); + + stat.endBatteryLevel = injectBatteryLevel(); + stat.endTime = injectCurrentTime(); + + final long deltaTime = stat.endTime - stat.startTime; + final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel; + + stat.totalTimeMillis += deltaTime; + stat.totalBatteryDrain += deltaDrain; + + if (DEBUG) { + Slog.d(TAG, "State summary: " + stateToString(mCurrentState) + + ": " + (deltaTime / 1_000) + "s " + + "Start level: " + stat.startBatteryLevel + "uA " + + "End level: " + stat.endBatteryLevel + "uA " + + deltaDrain + "uA"); + } + EventLogTags.writeBatterySavingStats( + BatterySaverState.fromIndex(mCurrentState), + InteractiveState.fromIndex(mCurrentState), + DozeState.fromIndex(mCurrentState), + deltaTime, + deltaDrain, + stat.totalTimeMillis, + stat.totalBatteryDrain); + } + + private void startNewStateLocked(int newState) { + if (DEBUG) { + Slog.d(TAG, "New state: " + stateToString(newState)); + } + mCurrentState = newState; + + if (mCurrentState < 0) { + return; + } + + final Stat stat = getStat(mCurrentState); + stat.startBatteryLevel = injectBatteryLevel(); + stat.startTime = injectCurrentTime(); + stat.endTime = 0; + } + + public void dump(PrintWriter pw, String indent) { + synchronized (mLock) { + pw.print(indent); + pw.println("Battery Saving Stats:"); + + indent = indent + " "; + + pw.print(indent); + pw.println("Battery Saver: Off On"); + dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + DozeState.NOT_DOZING, "NonDoze"); + dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + DozeState.NOT_DOZING, " "); + + dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + DozeState.DEEP, "Deep "); + dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + DozeState.DEEP, " "); + + dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + DozeState.LIGHT, "Light "); + dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + DozeState.LIGHT, " "); + + pw.println(); + } + } + + private void dumpLineLocked(PrintWriter pw, String indent, + int interactiveState, String interactiveLabel, + int dozeState, String dozeLabel) { + pw.print(indent); + pw.print(dozeLabel); + pw.print(" "); + pw.print(interactiveLabel); + pw.print(": "); + + final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState); + final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState); + + pw.println(String.format("%6dm %6dmA %8.1fmA/h %6dm %6dmA %8.1fmA/h", + offStat.totalMinutes(), + offStat.totalBatteryDrain / 1000, + offStat.drainPerHour() / 1000.0, + onStat.totalMinutes(), + onStat.totalBatteryDrain / 1000, + onStat.drainPerHour() / 1000.0)); + } +} + diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java new file mode 100644 index 000000000000..f788caccbdce --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 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.server.power.batterysaver; + +import static org.junit.Assert.assertEquals; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState; +import com.android.server.power.batterysaver.BatterySavingStats.DozeState; +import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; + +/** + atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BatterySavingStatsTest { + private class BatterySavingStatsTestable extends BatterySavingStats { + private long mTime = 1_000_000; // Some random starting time. + + private int mBatteryLevel = 100; + + @Override + long injectCurrentTime() { + return mTime; + } + + @Override + int injectBatteryLevel() { + return mBatteryLevel; + } + + void assertDumpable() { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + dump(new PrintWriter(out), ""); // Just make sure it won't crash. + } + + void advanceClock(int minutes) { + mTime += 60_000 * minutes; + } + + void drainBattery(int percent) { + mBatteryLevel -= percent; + if (mBatteryLevel < 0) { + mBatteryLevel = 0; + } + } + + String toDebugString() { + final StringBuilder sb = new StringBuilder(); + String sep = ""; + for (int i = 0; i < mStats.size(); i++) { + sb.append(sep); + sb.append(stateToString(mStats.keyAt(i))); + sb.append(":"); + sb.append(mStats.valueAt(i).toStringForTest()); + sep = "\n"; + } + return sb.toString(); + } + } + + @Test + public void testAll() { + final BatterySavingStatsTestable target = new BatterySavingStatsTestable(); + + target.assertDumpable(); + + target.advanceClock(1); + target.drainBattery(2); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(4); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(2); + target.drainBattery(5); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(4); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(2); + target.drainBattery(5); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(3); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.LIGHT); + + target.advanceClock(5); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.DEEP); + + target.advanceClock(1); + target.drainBattery(2); + + target.transitionState( + BatterySaverState.ON, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(1); + target.drainBattery(3); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(3); + target.drainBattery(5); + + target.transitionState( + BatterySaverState.ON, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(3); + target.drainBattery(5); + + target.startCharging(); + + target.advanceClock(5); + target.drainBattery(10); + + target.transitionState( + BatterySaverState.ON, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(5); + target.drainBattery(1); + + target.startCharging(); + + target.assertDumpable(); + + assertEquals( + "BS=0,I=0,D=0:{4m,10,150.00}\n" + + "BS=1,I=0,D=0:{0m,0,0.00}\n" + + "BS=0,I=1,D=0:{14m,8,34.29}\n" + + "BS=1,I=1,D=0:{9m,9,60.00}\n" + + "BS=0,I=0,D=1:{5m,1,12.00}\n" + + "BS=1,I=0,D=1:{0m,0,0.00}\n" + + "BS=0,I=1,D=1:{0m,0,0.00}\n" + + "BS=1,I=1,D=1:{0m,0,0.00}\n" + + "BS=0,I=0,D=2:{1m,2,120.00}\n" + + "BS=1,I=0,D=2:{0m,0,0.00}\n" + + "BS=0,I=1,D=2:{0m,0,0.00}\n" + + "BS=1,I=1,D=2:{0m,0,0.00}", + target.toDebugString()); + } +} |