Loading services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java +82 −21 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.power.BatterySaverPolicy; Loading Loading @@ -116,9 +117,16 @@ public class BatterySavingStats { } } @VisibleForTesting static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_"; @VisibleForTesting static final String COUNTER_TIME_SECONDS_PREFIX = "battery_saver_stats_seconds_"; private static BatterySavingStats sInstance; private BatteryManagerInternal mBatteryManagerInternal; private final MetricsLogger mMetricsLogger; private static final int STATE_NOT_INITIALIZED = -1; private static final int STATE_CHARGING = -2; Loading @@ -136,17 +144,21 @@ public class BatterySavingStats { @GuardedBy("mLock") final ArrayMap<Integer, Stat> mStats = new ArrayMap<>(); private final MetricsLoggerHelper mMetricsLoggerHelper = new MetricsLoggerHelper(); /** * Don't call it directly -- use {@link #getInstance()}. Not private for testing. * @param metricsLogger */ @VisibleForTesting BatterySavingStats() { BatterySavingStats(MetricsLogger metricsLogger) { mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); mMetricsLogger = metricsLogger; } public static synchronized BatterySavingStats getInstance() { if (sInstance == null) { sInstance = new BatterySavingStats(); sInstance = new BatterySavingStats(new MetricsLogger()); } return sInstance; } Loading Loading @@ -208,10 +220,12 @@ public class BatterySavingStats { return getStat(statesToIndex(batterySaverState, interactiveState, dozeState)); } @VisibleForTesting long injectCurrentTime() { return SystemClock.elapsedRealtime(); } @VisibleForTesting int injectBatteryLevel() { final BatteryManagerInternal bmi = getBatteryManagerInternal(); if (bmi == null) { Loading @@ -227,15 +241,9 @@ public class BatterySavingStats { */ 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); transitionStateLocked(newState); } } Loading @@ -244,23 +252,30 @@ public class BatterySavingStats { */ public void startCharging() { synchronized (mLock) { if (mCurrentState < 0) { return; transitionStateLocked(STATE_CHARGING); } } endLastStateLocked(); startNewStateLocked(STATE_CHARGING); private void transitionStateLocked(int newState) { if (mCurrentState == newState) { return; } final long now = injectCurrentTime(); final int batteryLevel = injectBatteryLevel(); endLastStateLocked(now, batteryLevel); startNewStateLocked(newState, now, batteryLevel); mMetricsLoggerHelper.transitionState(newState, now, batteryLevel); } private void endLastStateLocked() { private void endLastStateLocked(long now, int batteryLevel) { if (mCurrentState < 0) { return; } final Stat stat = getStat(mCurrentState); stat.endBatteryLevel = injectBatteryLevel(); stat.endTime = injectCurrentTime(); stat.endBatteryLevel = batteryLevel; stat.endTime = now; final long deltaTime = stat.endTime - stat.startTime; final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel; Loading @@ -283,9 +298,10 @@ public class BatterySavingStats { deltaDrain, stat.totalTimeMillis, stat.totalBatteryDrain); } private void startNewStateLocked(int newState) { private void startNewStateLocked(int newState, long now, int batteryLevel) { if (DEBUG) { Slog.d(TAG, "New state: " + stateToString(newState)); } Loading @@ -296,8 +312,8 @@ public class BatterySavingStats { } final Stat stat = getStat(mCurrentState); stat.startBatteryLevel = injectBatteryLevel(); stat.startTime = injectCurrentTime(); stat.startBatteryLevel = batteryLevel; stat.startTime = now; stat.endTime = 0; } Loading Loading @@ -349,5 +365,50 @@ public class BatterySavingStats { onStat.totalBatteryDrain / 1000, onStat.drainPerHour() / 1000.0)); } @VisibleForTesting class MetricsLoggerHelper { private int mLastState = STATE_NOT_INITIALIZED; private long mStartTime; private int mStartBatteryLevel; private static final int STATE_CHANGE_DETECT_MASK = (BatterySaverState.MASK << BatterySaverState.SHIFT) | (InteractiveState.MASK << InteractiveState.SHIFT); public void transitionState(int newState, long now, int batteryLevel) { final boolean stateChanging = ((mLastState >= 0) ^ (newState >= 0)) || (((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0); if (stateChanging) { if (mLastState >= 0) { final long deltaTime = now - mStartTime; final int deltaBattery = mStartBatteryLevel - batteryLevel; report(mLastState, deltaTime, deltaBattery); } mStartTime = now; mStartBatteryLevel = batteryLevel; } mLastState = newState; } String getCounterSuffix(int state) { final boolean batterySaver = BatterySaverState.fromIndex(state) != BatterySaverState.OFF; final boolean interactive = InteractiveState.fromIndex(state) != InteractiveState.NON_INTERACTIVE; if (batterySaver) { return interactive ? "11" : "10"; } else { return interactive ? "01" : "00"; } } void report(int state, long deltaTimeMs, int deltaBatteryUa) { final String suffix = getCounterSuffix(state); mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000); mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000)); } } } services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java +108 −3 Original line number Diff line number Diff line Loading @@ -16,10 +16,18 @@ package com.android.server.power.batterysaver; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.android.internal.logging.MetricsLogger; import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState; import com.android.server.power.batterysaver.BatterySavingStats.DozeState; import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState; Loading @@ -39,7 +47,11 @@ public class BatterySavingStatsTest { private class BatterySavingStatsTestable extends BatterySavingStats { private long mTime = 1_000_000; // Some random starting time. private int mBatteryLevel = 100; private int mBatteryLevel = 1_000_000_000; private BatterySavingStatsTestable() { super(mMetricsLogger); } @Override long injectCurrentTime() { Loading @@ -60,8 +72,8 @@ public class BatterySavingStatsTest { mTime += 60_000 * minutes; } void drainBattery(int percent) { mBatteryLevel -= percent; void drainBattery(int value) { mBatteryLevel -= value; if (mBatteryLevel < 0) { mBatteryLevel = 0; } Loading @@ -81,6 +93,8 @@ public class BatterySavingStatsTest { } } public MetricsLogger mMetricsLogger = mock(MetricsLogger.class); @Test public void testAll() { final BatterySavingStatsTestable target = new BatterySavingStatsTestable(); Loading Loading @@ -202,4 +216,95 @@ public class BatterySavingStatsTest { "BS=1,I=1,D=2:{0m,0,0.00}", target.toDebugString()); } private void assertMetricsLog(String counter, int value) { verify(mMetricsLogger, times(1)).count(eq(counter), eq(value)); } @Test public void testMetricsLogger() { final BatterySavingStatsTestable target = new BatterySavingStatsTestable(); target.advanceClock(1); target.drainBattery(1000); target.transitionState( BatterySaverState.OFF, InteractiveState.INTERACTIVE, DozeState.NOT_DOZING); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.advanceClock(1); target.drainBattery(2000); reset(mMetricsLogger); target.transitionState( BatterySaverState.OFF, InteractiveState.NON_INTERACTIVE, DozeState.NOT_DOZING); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60); target.advanceClock(1); target.drainBattery(2000); reset(mMetricsLogger); target.transitionState( BatterySaverState.OFF, InteractiveState.NON_INTERACTIVE, DozeState.DEEP); target.advanceClock(1); target.drainBattery(2000); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.transitionState( BatterySaverState.OFF, InteractiveState.NON_INTERACTIVE, DozeState.LIGHT); target.advanceClock(1); target.drainBattery(2000); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.transitionState( BatterySaverState.ON, InteractiveState.INTERACTIVE, DozeState.NOT_DOZING); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3); target.advanceClock(10); target.drainBattery(10_000); reset(mMetricsLogger); target.startCharging(); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10); target.advanceClock(1); target.drainBattery(2000); reset(mMetricsLogger); target.transitionState( BatterySaverState.ON, InteractiveState.NON_INTERACTIVE, DozeState.NOT_DOZING); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.advanceClock(1); target.drainBattery(2000); target.startCharging(); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60); } } Loading
services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java +82 −21 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.power.BatterySaverPolicy; Loading Loading @@ -116,9 +117,16 @@ public class BatterySavingStats { } } @VisibleForTesting static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_"; @VisibleForTesting static final String COUNTER_TIME_SECONDS_PREFIX = "battery_saver_stats_seconds_"; private static BatterySavingStats sInstance; private BatteryManagerInternal mBatteryManagerInternal; private final MetricsLogger mMetricsLogger; private static final int STATE_NOT_INITIALIZED = -1; private static final int STATE_CHARGING = -2; Loading @@ -136,17 +144,21 @@ public class BatterySavingStats { @GuardedBy("mLock") final ArrayMap<Integer, Stat> mStats = new ArrayMap<>(); private final MetricsLoggerHelper mMetricsLoggerHelper = new MetricsLoggerHelper(); /** * Don't call it directly -- use {@link #getInstance()}. Not private for testing. * @param metricsLogger */ @VisibleForTesting BatterySavingStats() { BatterySavingStats(MetricsLogger metricsLogger) { mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); mMetricsLogger = metricsLogger; } public static synchronized BatterySavingStats getInstance() { if (sInstance == null) { sInstance = new BatterySavingStats(); sInstance = new BatterySavingStats(new MetricsLogger()); } return sInstance; } Loading Loading @@ -208,10 +220,12 @@ public class BatterySavingStats { return getStat(statesToIndex(batterySaverState, interactiveState, dozeState)); } @VisibleForTesting long injectCurrentTime() { return SystemClock.elapsedRealtime(); } @VisibleForTesting int injectBatteryLevel() { final BatteryManagerInternal bmi = getBatteryManagerInternal(); if (bmi == null) { Loading @@ -227,15 +241,9 @@ public class BatterySavingStats { */ 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); transitionStateLocked(newState); } } Loading @@ -244,23 +252,30 @@ public class BatterySavingStats { */ public void startCharging() { synchronized (mLock) { if (mCurrentState < 0) { return; transitionStateLocked(STATE_CHARGING); } } endLastStateLocked(); startNewStateLocked(STATE_CHARGING); private void transitionStateLocked(int newState) { if (mCurrentState == newState) { return; } final long now = injectCurrentTime(); final int batteryLevel = injectBatteryLevel(); endLastStateLocked(now, batteryLevel); startNewStateLocked(newState, now, batteryLevel); mMetricsLoggerHelper.transitionState(newState, now, batteryLevel); } private void endLastStateLocked() { private void endLastStateLocked(long now, int batteryLevel) { if (mCurrentState < 0) { return; } final Stat stat = getStat(mCurrentState); stat.endBatteryLevel = injectBatteryLevel(); stat.endTime = injectCurrentTime(); stat.endBatteryLevel = batteryLevel; stat.endTime = now; final long deltaTime = stat.endTime - stat.startTime; final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel; Loading @@ -283,9 +298,10 @@ public class BatterySavingStats { deltaDrain, stat.totalTimeMillis, stat.totalBatteryDrain); } private void startNewStateLocked(int newState) { private void startNewStateLocked(int newState, long now, int batteryLevel) { if (DEBUG) { Slog.d(TAG, "New state: " + stateToString(newState)); } Loading @@ -296,8 +312,8 @@ public class BatterySavingStats { } final Stat stat = getStat(mCurrentState); stat.startBatteryLevel = injectBatteryLevel(); stat.startTime = injectCurrentTime(); stat.startBatteryLevel = batteryLevel; stat.startTime = now; stat.endTime = 0; } Loading Loading @@ -349,5 +365,50 @@ public class BatterySavingStats { onStat.totalBatteryDrain / 1000, onStat.drainPerHour() / 1000.0)); } @VisibleForTesting class MetricsLoggerHelper { private int mLastState = STATE_NOT_INITIALIZED; private long mStartTime; private int mStartBatteryLevel; private static final int STATE_CHANGE_DETECT_MASK = (BatterySaverState.MASK << BatterySaverState.SHIFT) | (InteractiveState.MASK << InteractiveState.SHIFT); public void transitionState(int newState, long now, int batteryLevel) { final boolean stateChanging = ((mLastState >= 0) ^ (newState >= 0)) || (((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0); if (stateChanging) { if (mLastState >= 0) { final long deltaTime = now - mStartTime; final int deltaBattery = mStartBatteryLevel - batteryLevel; report(mLastState, deltaTime, deltaBattery); } mStartTime = now; mStartBatteryLevel = batteryLevel; } mLastState = newState; } String getCounterSuffix(int state) { final boolean batterySaver = BatterySaverState.fromIndex(state) != BatterySaverState.OFF; final boolean interactive = InteractiveState.fromIndex(state) != InteractiveState.NON_INTERACTIVE; if (batterySaver) { return interactive ? "11" : "10"; } else { return interactive ? "01" : "00"; } } void report(int state, long deltaTimeMs, int deltaBatteryUa) { final String suffix = getCounterSuffix(state); mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000); mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000)); } } }
services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java +108 −3 Original line number Diff line number Diff line Loading @@ -16,10 +16,18 @@ package com.android.server.power.batterysaver; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.android.internal.logging.MetricsLogger; import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState; import com.android.server.power.batterysaver.BatterySavingStats.DozeState; import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState; Loading @@ -39,7 +47,11 @@ public class BatterySavingStatsTest { private class BatterySavingStatsTestable extends BatterySavingStats { private long mTime = 1_000_000; // Some random starting time. private int mBatteryLevel = 100; private int mBatteryLevel = 1_000_000_000; private BatterySavingStatsTestable() { super(mMetricsLogger); } @Override long injectCurrentTime() { Loading @@ -60,8 +72,8 @@ public class BatterySavingStatsTest { mTime += 60_000 * minutes; } void drainBattery(int percent) { mBatteryLevel -= percent; void drainBattery(int value) { mBatteryLevel -= value; if (mBatteryLevel < 0) { mBatteryLevel = 0; } Loading @@ -81,6 +93,8 @@ public class BatterySavingStatsTest { } } public MetricsLogger mMetricsLogger = mock(MetricsLogger.class); @Test public void testAll() { final BatterySavingStatsTestable target = new BatterySavingStatsTestable(); Loading Loading @@ -202,4 +216,95 @@ public class BatterySavingStatsTest { "BS=1,I=1,D=2:{0m,0,0.00}", target.toDebugString()); } private void assertMetricsLog(String counter, int value) { verify(mMetricsLogger, times(1)).count(eq(counter), eq(value)); } @Test public void testMetricsLogger() { final BatterySavingStatsTestable target = new BatterySavingStatsTestable(); target.advanceClock(1); target.drainBattery(1000); target.transitionState( BatterySaverState.OFF, InteractiveState.INTERACTIVE, DozeState.NOT_DOZING); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.advanceClock(1); target.drainBattery(2000); reset(mMetricsLogger); target.transitionState( BatterySaverState.OFF, InteractiveState.NON_INTERACTIVE, DozeState.NOT_DOZING); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60); target.advanceClock(1); target.drainBattery(2000); reset(mMetricsLogger); target.transitionState( BatterySaverState.OFF, InteractiveState.NON_INTERACTIVE, DozeState.DEEP); target.advanceClock(1); target.drainBattery(2000); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.transitionState( BatterySaverState.OFF, InteractiveState.NON_INTERACTIVE, DozeState.LIGHT); target.advanceClock(1); target.drainBattery(2000); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.transitionState( BatterySaverState.ON, InteractiveState.INTERACTIVE, DozeState.NOT_DOZING); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3); target.advanceClock(10); target.drainBattery(10_000); reset(mMetricsLogger); target.startCharging(); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10); target.advanceClock(1); target.drainBattery(2000); reset(mMetricsLogger); target.transitionState( BatterySaverState.ON, InteractiveState.NON_INTERACTIVE, DozeState.NOT_DOZING); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); target.advanceClock(1); target.drainBattery(2000); target.startCharging(); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60); } }