diff options
4 files changed, 684 insertions, 37 deletions
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index a34c2b93e3bf..6cba76424e6d 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -77,6 +77,7 @@ import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.os.AtomicFile; import com.android.internal.os.BackgroundThread; @@ -104,6 +105,8 @@ import java.util.Arrays; /** * Keeps track of device idleness and drives low power mode based on that. + * + * Test: atest com.android.server.DeviceIdleControllerTest */ public class DeviceIdleController extends SystemService implements AnyMotionDetector.DeviceIdleCallback { @@ -148,21 +151,29 @@ public class DeviceIdleController extends SystemService private boolean mScreenLocked; /** Device is currently active. */ - private static final int STATE_ACTIVE = 0; + @VisibleForTesting + static final int STATE_ACTIVE = 0; /** Device is inactive (screen off, no motion) and we are waiting to for idle. */ - private static final int STATE_INACTIVE = 1; + @VisibleForTesting + static final int STATE_INACTIVE = 1; /** Device is past the initial inactive period, and waiting for the next idle period. */ - private static final int STATE_IDLE_PENDING = 2; + @VisibleForTesting + static final int STATE_IDLE_PENDING = 2; /** Device is currently sensing motion. */ - private static final int STATE_SENSING = 3; + @VisibleForTesting + static final int STATE_SENSING = 3; /** Device is currently finding location (and may still be sensing). */ - private static final int STATE_LOCATING = 4; + @VisibleForTesting + static final int STATE_LOCATING = 4; /** Device is in the idle state, trying to stay asleep as much as possible. */ - private static final int STATE_IDLE = 5; + @VisibleForTesting + static final int STATE_IDLE = 5; /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */ - private static final int STATE_IDLE_MAINTENANCE = 6; + @VisibleForTesting + static final int STATE_IDLE_MAINTENANCE = 6; - private static String stateToString(int state) { + @VisibleForTesting + static String stateToString(int state) { switch (state) { case STATE_ACTIVE: return "ACTIVE"; case STATE_INACTIVE: return "INACTIVE"; @@ -176,21 +187,30 @@ public class DeviceIdleController extends SystemService } /** Device is currently active. */ - private static final int LIGHT_STATE_ACTIVE = 0; + @VisibleForTesting + static final int LIGHT_STATE_ACTIVE = 0; /** Device is inactive (screen off) and we are waiting to for the first light idle. */ - private static final int LIGHT_STATE_INACTIVE = 1; + @VisibleForTesting + static final int LIGHT_STATE_INACTIVE = 1; /** Device is about to go idle for the first time, wait for current work to complete. */ - private static final int LIGHT_STATE_PRE_IDLE = 3; + @VisibleForTesting + static final int LIGHT_STATE_PRE_IDLE = 3; /** Device is in the light idle state, trying to stay asleep as much as possible. */ - private static final int LIGHT_STATE_IDLE = 4; + @VisibleForTesting + static final int LIGHT_STATE_IDLE = 4; /** Device is in the light idle state, we want to go in to idle maintenance but are * waiting for network connectivity before doing so. */ - private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5; + @VisibleForTesting + static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5; /** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */ - private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6; + @VisibleForTesting + static final int LIGHT_STATE_IDLE_MAINTENANCE = 6; /** Device light idle state is overriden, now applying deep doze state. */ - private static final int LIGHT_STATE_OVERRIDE = 7; - private static String lightStateToString(int state) { + @VisibleForTesting + static final int LIGHT_STATE_OVERRIDE = 7; + + @VisibleForTesting + static String lightStateToString(int state) { switch (state) { case LIGHT_STATE_ACTIVE: return "ACTIVE"; case LIGHT_STATE_INACTIVE: return "INACTIVE"; @@ -382,6 +402,8 @@ public class DeviceIdleController extends SystemService public void onAlarm() { if (mState == STATE_SENSING) { synchronized (DeviceIdleController.this) { + // Restart the device idle progression in case the device moved but the screen + // didn't turn on. becomeInactiveIfAppropriateLocked(); } } @@ -422,11 +444,16 @@ public class DeviceIdleController extends SystemService } }; - private final class MotionListener extends TriggerEventListener + @VisibleForTesting + final class MotionListener extends TriggerEventListener implements SensorEventListener { boolean active = false; + public boolean isActive() { + return active; + } + @Override public void onTrigger(TriggerEvent event) { synchronized (DeviceIdleController.this) { @@ -472,7 +499,7 @@ public class DeviceIdleController extends SystemService active = false; } } - private final MotionListener mMotionListener = new MotionListener(); + @VisibleForTesting final MotionListener mMotionListener = new MotionListener(); private final LocationListener mGenericLocationListener = new LocationListener() { @Override @@ -594,7 +621,7 @@ public class DeviceIdleController extends SystemService public float LIGHT_IDLE_FACTOR; /** - * This is the maximum time we will run in idle maintenence mode. + * This is the maximum time we will run in idle maintenance mode. * @see Settings.Global#DEVICE_IDLE_CONSTANTS * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT */ @@ -1360,6 +1387,45 @@ public class DeviceIdleController extends SystemService } } + static class Injector { + private final Context mContext; + + Injector(Context ctx) { + mContext = ctx; + } + + AlarmManager getAlarmManager() { + return mContext.getSystemService(AlarmManager.class); + } + + AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm, + AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) { + return new AnyMotionDetector(getPowerManager(), handler, sm, callback, angleThreshold); + } + + AppStateTracker getAppStateTracker(Context ctx, Looper looper) { + return new AppStateTracker(ctx, looper); + } + + ConnectivityService getConnectivityService() { + return (ConnectivityService) ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + } + + LocationManager getLocationManager() { + return mContext.getSystemService(LocationManager.class); + } + + MyHandler getHandler(DeviceIdleController ctlr) { + return ctlr.new MyHandler(BackgroundThread.getHandler().getLooper()); + } + + PowerManager getPowerManager() { + return mContext.getSystemService(PowerManager.class); + } + } + + private final Injector mInjector; + private ActivityTaskManagerInternal.ScreenObserver mScreenObserver = new ActivityTaskManagerInternal.ScreenObserver() { @Override @@ -1373,14 +1439,19 @@ public class DeviceIdleController extends SystemService } }; - public DeviceIdleController(Context context) { + @VisibleForTesting DeviceIdleController(Context context, Injector injector) { super(context); + mInjector = injector; mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml")); - mHandler = new MyHandler(BackgroundThread.getHandler().getLooper()); - mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper()); + mHandler = mInjector.getHandler(this); + mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper()); LocalServices.addService(AppStateTracker.class, mAppStateTracker); } + public DeviceIdleController(Context context) { + this(context, new Injector(context)); + } + boolean isAppOnWhitelistInternal(int appid) { synchronized (this) { return Arrays.binarySearch(mPowerSaveWhitelistAllAppIdArray, appid) >= 0; @@ -1459,20 +1530,19 @@ public class DeviceIdleController extends SystemService public void onBootPhase(int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { synchronized (this) { - mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); + mAlarmManager = mInjector.getAlarmManager(); mBatteryStats = BatteryStatsService.getService(); mLocalActivityManager = getLocalService(ActivityManagerInternal.class); mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class); mLocalPowerManager = getLocalService(PowerManagerInternal.class); - mPowerManager = getContext().getSystemService(PowerManager.class); + mPowerManager = mInjector.getPowerManager(); mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "deviceidle_maint"); mActiveIdleWakeLock.setReferenceCounted(false); mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "deviceidle_going_idle"); mGoingIdleWakeLock.setReferenceCounted(true); - mConnectivityService = (ConnectivityService)ServiceManager.getService( - Context.CONNECTIVITY_SERVICE); + mConnectivityService = mInjector.getConnectivityService(); mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface( ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class); @@ -1495,8 +1565,7 @@ public class DeviceIdleController extends SystemService if (getContext().getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) { - mLocationManager = (LocationManager) getContext().getSystemService( - Context.LOCATION_SERVICE); + mLocationManager = mInjector.getLocationManager(); mLocationRequest = new LocationRequest() .setQuality(LocationRequest.ACCURACY_FINE) .setInterval(0) @@ -1506,9 +1575,8 @@ public class DeviceIdleController extends SystemService float angleThreshold = getContext().getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f; - mAnyMotionDetector = new AnyMotionDetector( - (PowerManager) getContext().getSystemService(Context.POWER_SERVICE), - mHandler, mSensorManager, this, angleThreshold); + mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this, + angleThreshold); mAppStateTracker.onSystemServicesReady(); @@ -2005,6 +2073,11 @@ public class DeviceIdleController extends SystemService } } + @VisibleForTesting + boolean isScreenOn() { + return mScreenOn; + } + void updateInteractivityLocked() { // The interactivity state from the power manager tells us whether the display is // in a state that we need to keep things running so they will update at a normal @@ -2024,6 +2097,11 @@ public class DeviceIdleController extends SystemService } } + @VisibleForTesting + boolean isCharging() { + return mCharging; + } + void updateChargingLocked(boolean charging) { if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging); if (!charging && mCharging) { @@ -2071,6 +2149,18 @@ public class DeviceIdleController extends SystemService } } + /** Must only be used in tests. */ + @VisibleForTesting + void setDeepEnabledForTest(boolean enabled) { + mDeepEnabled = enabled; + } + + /** Must only be used in tests. */ + @VisibleForTesting + void setLightEnabledForTest(boolean enabled) { + mLightEnabled = enabled; + } + void becomeInactiveIfAppropriateLocked() { if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()"); if ((!mScreenOn && !mCharging) || mForceIdle) { @@ -2093,7 +2183,7 @@ public class DeviceIdleController extends SystemService } } - void resetIdleManagementLocked() { + private void resetIdleManagementLocked() { mNextIdlePendingDelay = 0; mNextIdleDelay = 0; mNextLightIdleDelay = 0; @@ -2104,7 +2194,7 @@ public class DeviceIdleController extends SystemService mAnyMotionDetector.stop(); } - void resetLightIdleManagementLocked() { + private void resetLightIdleManagementLocked() { cancelLightAlarmLocked(); } @@ -2117,6 +2207,11 @@ public class DeviceIdleController extends SystemService } } + @VisibleForTesting + int getLightState() { + return mLightState; + } + void stepLightIdleStateLocked(String reason) { if (mLightState == LIGHT_STATE_OVERRIDE) { // If we are already in deep device idle mode, then @@ -2200,6 +2295,18 @@ public class DeviceIdleController extends SystemService } } + /** Must only be used in tests. */ + @VisibleForTesting + void setLocationManagerForTest(LocationManager lm) { + mLocationManager = lm; + } + + @VisibleForTesting + int getState() { + return mState; + } + + @VisibleForTesting void stepIdleStateLocked(String reason) { if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState); EventLogTags.writeDeviceIdleStep(); diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java new file mode 100644 index 000000000000..66650700f599 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -0,0 +1,543 @@ +/* + * 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; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.DeviceIdleController.LIGHT_STATE_ACTIVE; +import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE; +import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE_MAINTENANCE; +import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE; +import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE; +import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE; +import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK; +import static com.android.server.DeviceIdleController.STATE_ACTIVE; +import static com.android.server.DeviceIdleController.STATE_IDLE; +import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE; +import static com.android.server.DeviceIdleController.STATE_IDLE_PENDING; +import static com.android.server.DeviceIdleController.STATE_INACTIVE; +import static com.android.server.DeviceIdleController.STATE_LOCATING; +import static com.android.server.DeviceIdleController.STATE_SENSING; +import static com.android.server.DeviceIdleController.lightStateToString; +import static com.android.server.DeviceIdleController.stateToString; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; + +import android.app.ActivityManagerInternal; +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.content.Context; +import android.hardware.SensorManager; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.PowerManagerInternal; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.net.NetworkPolicyManagerInternal; +import com.android.server.wm.ActivityTaskManagerInternal; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +/** + * Tests for {@link com.android.server.DeviceIdleController}. + */ +@RunWith(AndroidJUnit4.class) +public class DeviceIdleControllerTest { + private DeviceIdleController mDeviceIdleController; + private AnyMotionDetectorForTest mAnyMotionDetector; + private AppStateTrackerForTest mAppStateTracker; + + private MockitoSession mMockingSession; + @Mock + private PowerManager mPowerManager; + @Mock + private PowerManager.WakeLock mWakeLock; + @Mock + private AlarmManager mAlarmManager; + @Mock + private LocationManager mLocationManager; + @Mock + private IActivityManager mIActivityManager; + + class InjectorForTest extends DeviceIdleController.Injector { + + InjectorForTest(Context ctx) { + super(ctx); + } + + @Override + AlarmManager getAlarmManager() { + return mAlarmManager; + } + + @Override + AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm, + AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) { + return mAnyMotionDetector; + } + + @Override + AppStateTracker getAppStateTracker(Context ctx, Looper loop) { + return mAppStateTracker; + } + + @Override + ConnectivityService getConnectivityService() { + return null; + } + + @Override + LocationManager getLocationManager() { + return mLocationManager; + } + + @Override + DeviceIdleController.MyHandler getHandler(DeviceIdleController ctlr) { + return mock(DeviceIdleController.MyHandler.class); + } + + @Override + PowerManager getPowerManager() { + return mPowerManager; + } + } + + private class AnyMotionDetectorForTest extends AnyMotionDetector { + boolean isMonitoring = false; + + AnyMotionDetectorForTest() { + super(mPowerManager, mock(Handler.class), mock(SensorManager.class), + mock(DeviceIdleCallback.class), 0.5f); + } + + @Override + public void checkForAnyMotion() { + isMonitoring = true; + } + + @Override + public void stop() { + isMonitoring = false; + } + } + + private class AppStateTrackerForTest extends AppStateTracker { + AppStateTrackerForTest(Context ctx, Looper looper) { + super(ctx, looper); + } + + @Override + public void onSystemServicesReady() { + // Do nothing. + } + + @Override + IActivityManager injectIActivityManager() { + return mIActivityManager; + } + } + + @Before + public void setUp() { + mMockingSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .mockStatic(LocalServices.class) + .startMocking(); + doReturn(mock(ActivityManagerInternal.class)) + .when(() -> LocalServices.getService(ActivityManagerInternal.class)); + doReturn(mock(ActivityTaskManagerInternal.class)) + .when(() -> LocalServices.getService(ActivityTaskManagerInternal.class)); + doReturn(mock(PowerManagerInternal.class)) + .when(() -> LocalServices.getService(PowerManagerInternal.class)); + doReturn(mock(NetworkPolicyManagerInternal.class)) + .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class)); + when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock); + doNothing().when(mWakeLock).acquire(); + mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper()); + mAnyMotionDetector = new AnyMotionDetectorForTest(); + mDeviceIdleController = new DeviceIdleController(getContext(), + new InjectorForTest(getContext())); + spyOn(mDeviceIdleController); + doNothing().when(mDeviceIdleController).publishBinderService(any(), any()); + mDeviceIdleController.onStart(); + mDeviceIdleController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + mDeviceIdleController.setDeepEnabledForTest(true); + mDeviceIdleController.setLightEnabledForTest(true); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove + // it after each test, otherwise, subsequent tests will fail. + LocalServices.removeServiceForTest(AppStateTracker.class); + } + + @Test + public void testUpdateInteractivityLocked() { + doReturn(false).when(mPowerManager).isInteractive(); + mDeviceIdleController.updateInteractivityLocked(); + assertFalse(mDeviceIdleController.isScreenOn()); + + // Make sure setting false when screen is already off doesn't change anything. + doReturn(false).when(mPowerManager).isInteractive(); + mDeviceIdleController.updateInteractivityLocked(); + assertFalse(mDeviceIdleController.isScreenOn()); + + // Test changing from screen off to screen on. + doReturn(true).when(mPowerManager).isInteractive(); + mDeviceIdleController.updateInteractivityLocked(); + assertTrue(mDeviceIdleController.isScreenOn()); + + // Make sure setting true when screen is already on doesn't change anything. + doReturn(true).when(mPowerManager).isInteractive(); + mDeviceIdleController.updateInteractivityLocked(); + assertTrue(mDeviceIdleController.isScreenOn()); + + // Test changing from screen on to screen off. + doReturn(false).when(mPowerManager).isInteractive(); + mDeviceIdleController.updateInteractivityLocked(); + assertFalse(mDeviceIdleController.isScreenOn()); + } + + @Test + public void testUpdateChargingLocked() { + mDeviceIdleController.updateChargingLocked(false); + assertFalse(mDeviceIdleController.isCharging()); + + // Make sure setting false when charging is already off doesn't change anything. + mDeviceIdleController.updateChargingLocked(false); + assertFalse(mDeviceIdleController.isCharging()); + + // Test changing from charging off to charging on. + mDeviceIdleController.updateChargingLocked(true); + assertTrue(mDeviceIdleController.isCharging()); + + // Make sure setting true when charging is already on doesn't change anything. + mDeviceIdleController.updateChargingLocked(true); + assertTrue(mDeviceIdleController.isCharging()); + + // Test changing from charging on to charging off. + mDeviceIdleController.updateChargingLocked(false); + assertFalse(mDeviceIdleController.isCharging()); + } + + @Test + public void testStateActiveToStateInactive_ConditionsNotMet() { + mDeviceIdleController.becomeActiveLocked("testing", 0); + verifyStateConditions(STATE_ACTIVE); + + // State should stay ACTIVE with screen on and charging. + setChargingOn(true); + setScreenOn(true); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); + + // State should stay ACTIVE with charging on. + setChargingOn(true); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); + + // State should stay ACTIVE with screen on. + // Note the different operation order here makes sure the state doesn't change before test. + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); + } + + @Test + public void testLightStateActiveToLightStateInactive_ConditionsNotMet() { + mDeviceIdleController.becomeActiveLocked("testing", 0); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + + // State should stay ACTIVE with screen on and charging. + setChargingOn(true); + setScreenOn(true); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + + // State should stay ACTIVE with charging on. + setChargingOn(true); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + + // State should stay ACTIVE with screen on. + // Note the different operation order here makes sure the state doesn't change before test. + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + } + + @Test + public void testStateActiveToStateInactive_ConditionsMet() { + mDeviceIdleController.becomeActiveLocked("testing", 0); + verifyStateConditions(STATE_ACTIVE); + + setChargingOn(false); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_INACTIVE); + } + + @Test + public void testLightStateActiveToLightStateInactive_ConditionsMet() { + mDeviceIdleController.becomeActiveLocked("testing", 0); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + + setChargingOn(false); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + } + + @Test + public void testStepIdleStateLocked_InvalidStates() { + mDeviceIdleController.becomeActiveLocked("testing", 0); + mDeviceIdleController.stepIdleStateLocked("testing"); + // mDeviceIdleController.stepIdleStateLocked doesn't handle the ACTIVE case, so the state + // should stay as ACTIVE. + verifyStateConditions(STATE_ACTIVE); + } + + @Test + public void testStepIdleStateLocked_ValidStates_NoLocationManager() { + mDeviceIdleController.setLocationManagerForTest(null); + // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon. + doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime(); + // Set state to INACTIVE. + mDeviceIdleController.becomeActiveLocked("testing", 0); + setChargingOn(false); + setScreenOn(false); + verifyStateConditions(STATE_INACTIVE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_PENDING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_SENSING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + // No location manager, so SENSING should go straight to IDLE. + verifyStateConditions(STATE_IDLE); + + // Should just alternate between IDLE and IDLE_MAINTENANCE now. + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + } + + @Test + public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() { + // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon. + doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime(); + // Set state to INACTIVE. + mDeviceIdleController.becomeActiveLocked("testing", 0); + setChargingOn(false); + setScreenOn(false); + verifyStateConditions(STATE_INACTIVE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_PENDING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_SENSING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + // Location manager exists but there isn't a network or GPS provider, + // so SENSING should go straight to IDLE. + verifyStateConditions(STATE_IDLE); + + // Should just alternate between IDLE and IDLE_MAINTENANCE now. + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + } + + @Test + public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() { + doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString()); + // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon. + // TODO: add tests for when there's a wake-from-idle alarm coming soon. + doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime(); + // Set state to INACTIVE. + mDeviceIdleController.becomeActiveLocked("testing", 0); + setChargingOn(false); + setScreenOn(false); + verifyStateConditions(STATE_INACTIVE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_PENDING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_SENSING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + // Location manager exists with a provider, so SENSING should go to LOCATING. + verifyStateConditions(STATE_LOCATING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + + // Should just alternate between IDLE and IDLE_MAINTENANCE now. + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + } + + private void setChargingOn(boolean on) { + mDeviceIdleController.updateChargingLocked(on); + } + + private void setScreenOn(boolean on) { + doReturn(on).when(mPowerManager).isInteractive(); + mDeviceIdleController.updateInteractivityLocked(); + } + + private void verifyStateConditions(int expectedState) { + int curState = mDeviceIdleController.getState(); + assertEquals( + "Expected " + stateToString(expectedState) + " but was " + stateToString(curState), + expectedState, curState); + + switch (expectedState) { + case STATE_ACTIVE: + assertFalse(mDeviceIdleController.mMotionListener.isActive()); + assertFalse(mAnyMotionDetector.isMonitoring); + break; + case STATE_INACTIVE: + assertFalse(mDeviceIdleController.mMotionListener.isActive()); + assertFalse(mAnyMotionDetector.isMonitoring); + assertFalse(mDeviceIdleController.isCharging()); + assertFalse(mDeviceIdleController.isScreenOn()); + break; + case STATE_IDLE_PENDING: + assertTrue(mDeviceIdleController.mMotionListener.isActive()); + assertFalse(mAnyMotionDetector.isMonitoring); + assertFalse(mDeviceIdleController.isCharging()); + assertFalse(mDeviceIdleController.isScreenOn()); + break; + case STATE_SENSING: + assertTrue(mDeviceIdleController.mMotionListener.isActive()); + assertTrue(mAnyMotionDetector.isMonitoring); + assertFalse(mDeviceIdleController.isCharging()); + assertFalse(mDeviceIdleController.isScreenOn()); + break; + case STATE_LOCATING: + assertTrue(mDeviceIdleController.mMotionListener.isActive()); + assertTrue(mAnyMotionDetector.isMonitoring); + assertFalse(mDeviceIdleController.isCharging()); + assertFalse(mDeviceIdleController.isScreenOn()); + break; + case STATE_IDLE: + assertTrue(mDeviceIdleController.mMotionListener.isActive()); + assertFalse(mAnyMotionDetector.isMonitoring); + assertFalse(mDeviceIdleController.isCharging()); + assertFalse(mDeviceIdleController.isScreenOn()); + // Light state should be OVERRIDE at this point. + verifyLightStateConditions(LIGHT_STATE_OVERRIDE); + break; + case STATE_IDLE_MAINTENANCE: + assertTrue(mDeviceIdleController.mMotionListener.isActive()); + assertFalse(mAnyMotionDetector.isMonitoring); + assertFalse(mDeviceIdleController.isCharging()); + assertFalse(mDeviceIdleController.isScreenOn()); + break; + default: + fail("Conditions for " + stateToString(expectedState) + " unknown."); + } + } + + private void verifyLightStateConditions(int expectedLightState) { + int curLightState = mDeviceIdleController.getLightState(); + assertEquals( + "Expected " + lightStateToString(expectedLightState) + + " but was " + lightStateToString(curLightState), + expectedLightState, curLightState); + + switch (expectedLightState) { + case LIGHT_STATE_ACTIVE: + assertTrue( + mDeviceIdleController.isCharging() || mDeviceIdleController.isScreenOn()); + break; + case LIGHT_STATE_INACTIVE: + case LIGHT_STATE_PRE_IDLE: + case LIGHT_STATE_IDLE: + case LIGHT_STATE_WAITING_FOR_NETWORK: + case LIGHT_STATE_IDLE_MAINTENANCE: + case LIGHT_STATE_OVERRIDE: + assertFalse(mDeviceIdleController.isCharging()); + assertFalse(mDeviceIdleController.isScreenOn()); + break; + default: + fail("Conditions for " + lightStateToString(expectedLightState) + " unknown."); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java index 54ac6fc7db93..bd4a356fcb5c 100644 --- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java @@ -84,9 +84,6 @@ public class BatterySaverPolicyTest extends AndroidTestCase { } @Mock - Handler mHandler; - - @Mock MetricsLogger mMetricsLogger = mock(MetricsLogger.class); private BatterySaverPolicyForTest mBatterySaverPolicy; diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java index cf84c7926549..fff9635992d4 100644 --- a/tests/testables/src/android/testing/TestableContext.java +++ b/tests/testables/src/android/testing/TestableContext.java @@ -53,7 +53,7 @@ import org.junit.runners.model.Statement; * Like the following:</p> * <pre class="prettyprint"> * @Rule - * private final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext()); + * public final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext()); * </pre> */ public class TestableContext extends ContextWrapper implements TestRule { |