diff options
4 files changed, 340 insertions, 88 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index 275661084aa3..5b0de5e2aae0 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -26,6 +26,8 @@ public interface AlarmManagerInternal { void broadcastAlarmComplete(int recipientUid); } + /** Returns true if AlarmManager is delaying alarms due to device idle. */ + boolean isIdling(); public void removeAlarmsForUid(int uid); public void registerInFlightListener(InFlightListener callback); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 10b532700d1b..a400cc384a58 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1991,6 +1991,11 @@ class AlarmManagerService extends SystemService { */ private final class LocalService implements AlarmManagerInternal { @Override + public boolean isIdling() { + return isIdlingImpl(); + } + + @Override public void removeAlarmsForUid(int uid) { synchronized (mLock) { removeLocked(uid); @@ -2823,6 +2828,12 @@ class AlarmManagerService extends SystemService { } } + private boolean isIdlingImpl() { + synchronized (mLock) { + return mPendingIdleUntil != null; + } + } + AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) { synchronized (mLock) { return mNextAlarmClockForUser.get(userId); diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 39030aaf3eb4..6b663941f8fb 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -271,6 +271,7 @@ public class DeviceIdleController extends SystemService private static final int EVENT_BUFFER_SIZE = 100; private AlarmManager mAlarmManager; + private AlarmManagerInternal mLocalAlarmManager; private IBatteryStats mBatteryStats; private ActivityManagerInternal mLocalActivityManager; private ActivityTaskManagerInternal mLocalActivityTaskManager; @@ -616,7 +617,8 @@ public class DeviceIdleController extends SystemService } }; - private final AlarmManager.OnAlarmListener mDeepAlarmListener + @VisibleForTesting + final AlarmManager.OnAlarmListener mDeepAlarmListener = new AlarmManager.OnAlarmListener() { @Override public void onAlarm() { @@ -1874,6 +1876,7 @@ public class DeviceIdleController extends SystemService if (phase == PHASE_SYSTEM_SERVICES_READY) { synchronized (this) { mAlarmManager = mInjector.getAlarmManager(); + mLocalAlarmManager = getLocalService(AlarmManagerInternal.class); mBatteryStats = BatteryStatsService.getService(); mLocalActivityManager = getLocalService(ActivityManagerInternal.class); mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class); @@ -2605,6 +2608,16 @@ public class DeviceIdleController extends SystemService // next natural time to come out of it. } + + /** Returns true if the screen is locked. */ + @VisibleForTesting + boolean isKeyguardShowing() { + synchronized (this) { + return mScreenLocked; + } + } + + @VisibleForTesting void keyguardShowingLocked(boolean showing) { if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing); if (mScreenLocked != showing) { @@ -2616,25 +2629,38 @@ public class DeviceIdleController extends SystemService } } + @VisibleForTesting void scheduleReportActiveLocked(String activeReason, int activeUid) { Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason); mHandler.sendMessage(msg); } void becomeActiveLocked(String activeReason, int activeUid) { - if (DEBUG) Slog.i(TAG, "becomeActiveLocked, reason = " + activeReason); + becomeActiveLocked(activeReason, activeUid, mConstants.INACTIVE_TIMEOUT, true); + } + + private void becomeActiveLocked(String activeReason, int activeUid, + long newInactiveTimeout, boolean changeLightIdle) { + if (DEBUG) { + Slog.i(TAG, "becomeActiveLocked, reason=" + activeReason + + ", changeLightIdle=" + changeLightIdle); + } if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) { EventLogTags.writeDeviceIdle(STATE_ACTIVE, activeReason); - EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason); - scheduleReportActiveLocked(activeReason, activeUid); mState = STATE_ACTIVE; - mLightState = LIGHT_STATE_ACTIVE; - mInactiveTimeout = mConstants.INACTIVE_TIMEOUT; + mInactiveTimeout = newInactiveTimeout; mCurIdleBudget = 0; mMaintenanceStartTime = 0; resetIdleManagementLocked(); - resetLightIdleManagementLocked(); - addEvent(EVENT_NORMAL, activeReason); + + if (changeLightIdle) { + EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason); + mLightState = LIGHT_STATE_ACTIVE; + resetLightIdleManagementLocked(); + // Only report active if light is also ACTIVE. + scheduleReportActiveLocked(activeReason, activeUid); + addEvent(EVENT_NORMAL, activeReason); + } } } @@ -2654,50 +2680,82 @@ public class DeviceIdleController extends SystemService } } + /** Sanity check to make sure DeviceIdleController and AlarmManager are on the same page. */ + private void verifyAlarmStateLocked() { + if (mState == STATE_ACTIVE && mNextAlarmTime != 0) { + Slog.wtf(TAG, "mState=ACTIVE but mNextAlarmTime=" + mNextAlarmTime); + } + if (mState != STATE_IDLE && mLocalAlarmManager.isIdling()) { + Slog.wtf(TAG, "mState=" + stateToString(mState) + " but AlarmManager is idling"); + } + if (mState == STATE_IDLE && !mLocalAlarmManager.isIdling()) { + Slog.wtf(TAG, "mState=IDLE but AlarmManager is not idling"); + } + if (mLightState == LIGHT_STATE_ACTIVE && mNextLightAlarmTime != 0) { + Slog.wtf(TAG, "mLightState=ACTIVE but mNextLightAlarmTime is " + + TimeUtils.formatDuration(mNextLightAlarmTime - SystemClock.elapsedRealtime()) + + " from now"); + } + } + void becomeInactiveIfAppropriateLocked() { - if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()"); - if ((!mScreenOn && !mCharging) || mForceIdle) { - // Become inactive and determine if we will ultimately go idle. - if (mDeepEnabled) { - if (mQuickDozeActivated) { - if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE - || mState == STATE_IDLE_MAINTENANCE) { - // Already "idling". Don't want to restart the process. - // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3 - // values, so returning here is safe. - return; - } - if (DEBUG) { - Slog.d(TAG, "Moved from " - + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY"); - } - mState = STATE_QUICK_DOZE_DELAY; - // Make sure any motion sensing or locating is stopped. - resetIdleManagementLocked(); - // Wait a small amount of time in case something (eg: background service from - // recently closed app) needs to finish running. - scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); - EventLogTags.writeDeviceIdle(mState, "no activity"); - } else if (mState == STATE_ACTIVE) { - mState = STATE_INACTIVE; - if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE"); - resetIdleManagementLocked(); - long delay = mInactiveTimeout; - if (shouldUseIdleTimeoutFactorLocked()) { - delay = (long) (mPreIdleFactor * delay); - } - scheduleAlarmLocked(delay, false); - EventLogTags.writeDeviceIdle(mState, "no activity"); + verifyAlarmStateLocked(); + + final boolean isScreenBlockingInactive = + mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked); + if (DEBUG) { + Slog.d(TAG, "becomeInactiveIfAppropriateLocked():" + + " isScreenBlockingInactive=" + isScreenBlockingInactive + + " (mScreenOn=" + mScreenOn + + ", WAIT_FOR_UNLOCK=" + mConstants.WAIT_FOR_UNLOCK + + ", mScreenLocked=" + mScreenLocked + ")" + + " mCharging=" + mCharging + + " mForceIdle=" + mForceIdle + ); + } + if (!mForceIdle && (mCharging || isScreenBlockingInactive)) { + return; + } + // Become inactive and determine if we will ultimately go idle. + if (mDeepEnabled) { + if (mQuickDozeActivated) { + if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE + || mState == STATE_IDLE_MAINTENANCE) { + // Already "idling". Don't want to restart the process. + // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3 + // values, so returning here is safe. + return; } - } - if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) { - mLightState = LIGHT_STATE_INACTIVE; - if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE"); - resetLightIdleManagementLocked(); - scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT); - EventLogTags.writeDeviceIdleLight(mLightState, "no activity"); + if (DEBUG) { + Slog.d(TAG, "Moved from " + + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY"); + } + mState = STATE_QUICK_DOZE_DELAY; + // Make sure any motion sensing or locating is stopped. + resetIdleManagementLocked(); + // Wait a small amount of time in case something (eg: background service from + // recently closed app) needs to finish running. + scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + EventLogTags.writeDeviceIdle(mState, "no activity"); + } else if (mState == STATE_ACTIVE) { + mState = STATE_INACTIVE; + if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE"); + resetIdleManagementLocked(); + long delay = mInactiveTimeout; + if (shouldUseIdleTimeoutFactorLocked()) { + delay = (long) (mPreIdleFactor * delay); + } + scheduleAlarmLocked(delay, false); + EventLogTags.writeDeviceIdle(mState, "no activity"); } } + if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) { + mLightState = LIGHT_STATE_INACTIVE; + if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE"); + resetLightIdleManagementLocked(); + scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT); + EventLogTags.writeDeviceIdleLight(mLightState, "no activity"); + } } private void resetIdleManagementLocked() { @@ -3216,33 +3274,10 @@ public class DeviceIdleController extends SystemService // The device is not yet active, so we want to go back to the pending idle // state to wait again for no motion. Note that we only monitor for motion // after moving out of the inactive state, so no need to worry about that. - boolean becomeInactive = false; - if (mState != STATE_ACTIVE) { - // Motion shouldn't affect light state, if it's already in doze-light or maintenance - boolean lightIdle = mLightState == LIGHT_STATE_IDLE - || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK - || mLightState == LIGHT_STATE_IDLE_MAINTENANCE; - if (!lightIdle) { - // Only switch to active state if we're not in either idle state - scheduleReportActiveLocked(type, Process.myUid()); - addEvent(EVENT_NORMAL, type); - } - mActiveReason = ACTIVE_REASON_MOTION; - mState = STATE_ACTIVE; - mInactiveTimeout = timeout; - mCurIdleBudget = 0; - mMaintenanceStartTime = 0; - EventLogTags.writeDeviceIdle(mState, type); - becomeInactive = true; - updateActiveConstraintsLocked(); - } - if (mLightState == LIGHT_STATE_OVERRIDE) { - // We went out of light idle mode because we had started deep idle mode... let's - // now go back and reset things so we resume light idling if appropriate. - mLightState = LIGHT_STATE_ACTIVE; - EventLogTags.writeDeviceIdleLight(mLightState, type); - becomeInactive = true; - } + final boolean becomeInactive = mState != STATE_ACTIVE + || mLightState == LIGHT_STATE_OVERRIDE; + // We only want to change the IDLE state if it's OVERRIDE. + becomeActiveLocked(type, Process.myUid(), timeout, mLightState == LIGHT_STATE_OVERRIDE); if (becomeInactive) { becomeInactiveIfAppropriateLocked(); } diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 53d72bb9a415..e51ee947cba1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -19,6 +19,7 @@ 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.inOrder; 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; @@ -51,6 +52,9 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import android.app.ActivityManagerInternal; import android.app.AlarmManager; @@ -82,6 +86,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; @@ -105,6 +110,8 @@ public class DeviceIdleControllerTest { @Mock private ContentResolver mContentResolver; @Mock + private DeviceIdleController.MyHandler mHandler; + @Mock private IActivityManager mIActivityManager; @Mock private LocationManager mLocationManager; @@ -154,7 +161,7 @@ public class DeviceIdleControllerTest { @Override DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) { - return mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS); + return mHandler; } @Override @@ -232,10 +239,12 @@ public class DeviceIdleControllerTest { .when(() -> LocalServices.getService(ActivityManagerInternal.class)); doReturn(mock(ActivityTaskManagerInternal.class)) .when(() -> LocalServices.getService(ActivityTaskManagerInternal.class)); + doReturn(mock(AlarmManagerInternal.class)) + .when(() -> LocalServices.getService(AlarmManagerInternal.class)); doReturn(mPowerManagerInternal) .when(() -> LocalServices.getService(PowerManagerInternal.class)); - when(mPowerManagerInternal.getLowPowerState(anyInt())).thenReturn( - mock(PowerSaveState.class)); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(mock(PowerSaveState.class)); doReturn(mock(NetworkPolicyManagerInternal.class)) .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class)); when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock); @@ -246,8 +255,11 @@ public class DeviceIdleControllerTest { doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt()); mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper()); mAnyMotionDetector = new AnyMotionDetectorForTest(); + mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS); + doNothing().when(mHandler).handleMessage(any()); mInjector = new InjectorForTest(getContext()); doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any()); + mDeviceIdleController = new DeviceIdleController(getContext(), mInjector); spyOn(mDeviceIdleController); doNothing().when(mDeviceIdleController).publishBinderService(any(), any()); @@ -423,6 +435,29 @@ public class DeviceIdleControllerTest { mDeviceIdleController.becomeInactiveIfAppropriateLocked(); verifyStateConditions(STATE_ACTIVE); + + mConstants.WAIT_FOR_UNLOCK = false; + setScreenLocked(true); + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); + + setScreenLocked(false); + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); + + mConstants.WAIT_FOR_UNLOCK = true; + setScreenLocked(false); + setScreenOn(true); + setChargingOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_ACTIVE); } @Test @@ -1307,7 +1342,7 @@ public class DeviceIdleControllerTest { } @Test - public void testbecomeActiveLocked_deep() { + public void testBecomeActiveLocked_deep() { // becomeActiveLocked should put everything into ACTIVE. enterDeepState(STATE_ACTIVE); @@ -1344,7 +1379,7 @@ public class DeviceIdleControllerTest { } @Test - public void testbecomeActiveLocked_light() { + public void testBecomeActiveLocked_light() { // becomeActiveLocked should put everything into ACTIVE. enterLightState(LIGHT_STATE_ACTIVE); @@ -1376,6 +1411,163 @@ public class DeviceIdleControllerTest { verifyLightStateConditions(LIGHT_STATE_ACTIVE); } + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOn_ScreenThenMotion() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(true); + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen locked, turning the screen on by itself + // shouldn't bring the device out of deep IDLE. + verifyStateConditions(STATE_IDLE); + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // Motion should bring the device out of Doze. Since the screen is still locked (albeit + // on), the states should go back into INACTIVE. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt()); + } + + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOff_ScreenThenMotion() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(false); + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen unlocked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt()); + } + + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOn_MotionThenScreen() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + InOrder controllerInOrder = inOrder(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(true); + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // The screen is still off, so motion should result in the INACTIVE state. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen locked, turning the screen on by itself + // shouldn't bring the device all the way to ACTIVE. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager, never()).cancel( + eq(mDeviceIdleController.mDeepAlarmListener)); + + // User finally unlocks the device. Device should be fully active. + mDeviceIdleController.keyguardShowingLocked(false); + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + } + + /** Test based on b/119058625. */ + @Test + public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOff_MotionThenScreen() { + mConstants.WAIT_FOR_UNLOCK = true; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + InOrder controllerInOrder = inOrder(mDeviceIdleController); + + mDeviceIdleController.keyguardShowingLocked(false); + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // The screen is still off, so motion should result in the INACTIVE state. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = true and the screen unlocked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + } + + @Test + public void testExitNotifiesDependencies_WaitForUnlockOff_Screen() { + mConstants.WAIT_FOR_UNLOCK = false; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = false and the screen locked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt()); + } + + @Test + public void testExitNotifiesDependencies_WaitForUnlockOff_MotionThenScreen() { + mConstants.WAIT_FOR_UNLOCK = false; + enterDeepState(STATE_IDLE); + reset(mAlarmManager); + spyOn(mDeviceIdleController); + + InOrder alarmManagerInOrder = inOrder(mAlarmManager); + InOrder controllerInOrder = inOrder(mDeviceIdleController); + + mDeviceIdleController.handleMotionDetectedLocked(1000, "test"); + // The screen is still off, so motion should result in the INACTIVE state. + verifyStateConditions(STATE_INACTIVE); + verifyLightStateConditions(LIGHT_STATE_INACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + + setScreenOn(true); + // With WAIT_FOR_UNLOCK = false and the screen locked, turning the screen on by itself + // should bring the device out of deep IDLE. + verifyStateConditions(STATE_ACTIVE); + verifyLightStateConditions(LIGHT_STATE_ACTIVE); + alarmManagerInOrder.verify(mAlarmManager) + .cancel(eq(mDeviceIdleController.mDeepAlarmListener)); + controllerInOrder.verify(mDeviceIdleController) + .scheduleReportActiveLocked(anyString(), anyInt()); + } + @Test public void testStepToIdleMode() { float delta = mDeviceIdleController.MIN_PRE_IDLE_FACTOR_CHANGE; @@ -1508,6 +1700,10 @@ public class DeviceIdleControllerTest { mDeviceIdleController.updateChargingLocked(on); } + private void setScreenLocked(boolean locked) { + mDeviceIdleController.keyguardShowingLocked(locked); + } + private void setScreenOn(boolean on) { doReturn(on).when(mPowerManager).isInteractive(); mDeviceIdleController.updateInteractivityLocked(); @@ -1549,7 +1745,8 @@ public class DeviceIdleControllerTest { assertFalse(mDeviceIdleController.mMotionListener.isActive()); assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_IDLE_PENDING: assertEquals( @@ -1557,7 +1754,8 @@ public class DeviceIdleControllerTest { mDeviceIdleController.mMotionListener.isActive()); assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_SENSING: assertEquals( @@ -1567,14 +1765,16 @@ public class DeviceIdleControllerTest { mDeviceIdleController.hasMotionSensor(), mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_LOCATING: assertEquals( mDeviceIdleController.hasMotionSensor(), mDeviceIdleController.mMotionListener.isActive()); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_IDLE: if (mDeviceIdleController.hasMotionSensor()) { @@ -1584,7 +1784,8 @@ public class DeviceIdleControllerTest { } assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); // Light state should be OVERRIDE at this point. verifyLightStateConditions(LIGHT_STATE_OVERRIDE); break; @@ -1596,14 +1797,16 @@ public class DeviceIdleControllerTest { } assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; case STATE_QUICK_DOZE_DELAY: // If quick doze is enabled, the motion listener should NOT be active. assertFalse(mDeviceIdleController.mMotionListener.isActive()); assertFalse(mAnyMotionDetector.isMonitoring); assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; default: fail("Conditions for " + stateToString(expectedState) + " unknown."); @@ -1632,7 +1835,8 @@ public class DeviceIdleControllerTest { case LIGHT_STATE_IDLE_MAINTENANCE: case LIGHT_STATE_OVERRIDE: assertFalse(mDeviceIdleController.isCharging()); - assertFalse(mDeviceIdleController.isScreenOn()); + assertFalse(mDeviceIdleController.isScreenOn() + && !mDeviceIdleController.isKeyguardShowing()); break; default: fail("Conditions for " + lightStateToString(expectedLightState) + " unknown."); |