/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.doze; import static android.content.res.Configuration.UI_MODE_NIGHT_YES; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING_BRIGHT; import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE; import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS; import static com.android.systemui.doze.DozeMachine.State.FINISH; import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; import android.testing.AndroidTestingRunner; import android.testing.UiThreadTest; import android.view.Display; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.WakeLockFake; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @UiThreadTest public class DozeMachineTest extends SysuiTestCase { DozeMachine mMachine; @Mock private WakefulnessLifecycle mWakefulnessLifecycle; @Mock private DozeLog mDozeLog; @Mock private DockManager mDockManager; @Mock private DozeHost mHost; @Mock private DozeMachine.Part mPartMock; @Mock private DozeMachine.Part mAnotherPartMock; @Mock private UserTracker mUserTracker; private DozeServiceFake mServiceFake; private WakeLockFake mWakeLockFake; private AmbientDisplayConfiguration mAmbientDisplayConfigMock; @Before public void setUp() { MockitoAnnotations.initMocks(this); mServiceFake = new DozeServiceFake(); mWakeLockFake = new WakeLockFake(); mAmbientDisplayConfigMock = mock(AmbientDisplayConfiguration.class); when(mDockManager.isDocked()).thenReturn(false); when(mDockManager.isHidden()).thenReturn(false); when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser()); mMachine = new DozeMachine(mServiceFake, mAmbientDisplayConfigMock, mWakeLockFake, mWakefulnessLifecycle, mDozeLog, mDockManager, mHost, new DozeMachine.Part[]{mPartMock, mAnotherPartMock}, mUserTracker); } @Test public void testInitialize_initializesParts() { mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(UNINITIALIZED, INITIALIZED); } @Test public void testInitialize_goesToDoze() { when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testInitialize_goesToAod() { when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE_AOD); assertEquals(DOZE_AOD, mMachine.getState()); } @Test public void testInitialize_afterDocked_goesToDockedAod() { when(mDockManager.isDocked()).thenReturn(true); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE_AOD_DOCKED); assertEquals(DOZE_AOD_DOCKED, mMachine.getState()); } @Test public void testInitialize_afterDockPaused_goesToDoze() { when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); when(mDockManager.isDocked()).thenReturn(true); when(mDockManager.isHidden()).thenReturn(true); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testInitialize_alwaysOnSuppressed_alwaysOnDisabled_goesToDoze() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testInitialize_alwaysOnSuppressed_alwaysOnEnabled_goesToDoze() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testInitialize_alwaysOnSuppressed_afterDocked_goesToDoze() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mDockManager.isDocked()).thenReturn(true); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testInitialize_alwaysOnSuppressed_alwaysOnDisabled_afterDockPaused_goesToDoze() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false); when(mDockManager.isDocked()).thenReturn(true); when(mDockManager.isHidden()).thenReturn(true); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testInitialize_alwaysOnSuppressed_alwaysOnEnabled_afterDockPaused_goesToDoze() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); when(mDockManager.isDocked()).thenReturn(true); when(mDockManager.isHidden()).thenReturn(true); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testPulseDone_goesToDoze() { when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false); mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testPulseDone_goesToAoD() { when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE_AOD); assertEquals(DOZE_AOD, mMachine.getState()); } @Test public void testPulseDone_alwaysOnSuppressed_goesToSuppressed() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testPulseDone_afterDocked_goesToDockedAoD() { when(mDockManager.isDocked()).thenReturn(true); mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE_AOD_DOCKED); assertEquals(DOZE_AOD_DOCKED, mMachine.getState()); } @Test public void testPulseDone_whileDockedAoD_staysDockedAod() { when(mDockManager.isDocked()).thenReturn(true); mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE_AOD_DOCKED); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock, never()).transitionTo(DOZE_AOD_DOCKED, DOZE_PULSE_DONE); } @Test public void testPulseDone_alwaysOnSuppressed_afterDocked_goesToDoze() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mDockManager.isDocked()).thenReturn(true); mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testPulseDone_afterDockPaused_goesToDoze() { when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); when(mDockManager.isDocked()).thenReturn(true); when(mDockManager.isHidden()).thenReturn(true); mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testPulseDone_alwaysOnSuppressed_afterDockPaused_goesToDoze() { when(mHost.isAlwaysOnSuppressed()).thenReturn(true); when(mAmbientDisplayConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true); when(mDockManager.isDocked()).thenReturn(true); when(mDockManager.isHidden()).thenReturn(true); mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE); assertEquals(DOZE, mMachine.getState()); } @Test public void testFinished_staysFinished() { mMachine.requestState(INITIALIZED); mMachine.requestState(FINISH); reset(mPartMock); mMachine.requestState(DOZE); verify(mPartMock, never()).transitionTo(any(), any()); assertEquals(FINISH, mMachine.getState()); } @Test public void testFinish_finishesService() { mMachine.requestState(INITIALIZED); mMachine.requestState(FINISH); assertTrue(mServiceFake.finished); } @Test public void testWakeLock_heldInTransition() { doAnswer((inv) -> { assertTrue(mWakeLockFake.isHeld()); return null; }).when(mPartMock).transitionTo(any(), any()); mMachine.requestState(INITIALIZED); } @Test public void testWakeLock_heldInPulseStates() { mMachine.requestState(INITIALIZED); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); assertTrue(mWakeLockFake.isHeld()); mMachine.requestState(DOZE_PULSING); assertTrue(mWakeLockFake.isHeld()); } @Test public void testWakeLock_notHeldInDozeStates() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); assertFalse(mWakeLockFake.isHeld()); mMachine.requestState(DOZE_AOD); assertFalse(mWakeLockFake.isHeld()); } @Test public void testWakeLock_releasedAfterPulse() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); assertFalse(mWakeLockFake.isHeld()); } @Test public void testPulseDuringPulse_doesntCrash() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSE_DONE); } @Test public void testPulsing_dozeSuspendTriggers_pulseDone_doesntCrash() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_SUSPEND_TRIGGERS); mMachine.requestState(DOZE_PULSE_DONE); } @Test public void testSuppressingPulse_doesntCrash() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSE_DONE); } @Test public void testTransitions_canRequestTransitions() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); doAnswer(inv -> { mMachine.requestState(DOZE_PULSING); return null; }).when(mPartMock).transitionTo(any(), eq(DOZE_REQUEST_PULSE)); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); assertEquals(DOZE_PULSING, mMachine.getState()); } @Test public void testPulseReason_getMatchesRequest() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); mMachine.requestPulse(DozeLog.REASON_SENSOR_DOUBLE_TAP); assertEquals(DozeLog.REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason()); } @Test public void testPulseReason_getFromTransition() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); doAnswer(inv -> { DozeMachine.State newState = inv.getArgument(1); if (newState == DOZE_REQUEST_PULSE || newState == DOZE_PULSING || newState == DOZE_PULSE_DONE) { assertEquals(DozeLog.PULSE_REASON_NOTIFICATION, mMachine.getPulseReason()); } else { assertTrue("unexpected state " + newState, newState == DOZE || newState == DOZE_AOD); } return null; }).when(mPartMock).transitionTo(any(), any()); mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); mMachine.requestState(DOZE_PULSING); mMachine.requestState(DOZE_PULSE_DONE); } @Test public void testWakeUp_wakesUp() { mMachine.wakeUp(DozeLog.REASON_SENSOR_PICKUP); assertTrue(mServiceFake.requestedWakeup); } @Test public void testDozePulsing_displayRequiresBlanking_screenState() { DozeParameters dozeParameters = mock(DozeParameters.class); when(dozeParameters.getDisplayNeedsBlanking()).thenReturn(true); assertEquals(Display.STATE_OFF, DOZE_REQUEST_PULSE.screenState(dozeParameters)); } @Test public void testDozePulsing_displayDoesNotRequireBlanking_screenState() { DozeParameters dozeParameters = mock(DozeParameters.class); when(dozeParameters.getDisplayNeedsBlanking()).thenReturn(false); assertEquals(Display.STATE_ON, DOZE_REQUEST_PULSE.screenState(dozeParameters)); } @Test public void testTransitionToInitialized_carModeIsEnabled() { Configuration configuration = configWithCarNightUiMode(); mMachine.onConfigurationChanged(configuration); mMachine.requestState(INITIALIZED); verify(mPartMock).transitionTo(UNINITIALIZED, INITIALIZED); verify(mPartMock).transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS); assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); } @Test public void testTransitionToFinish_carModeIsEnabled() { Configuration configuration = configWithCarNightUiMode(); mMachine.onConfigurationChanged(configuration); mMachine.requestState(INITIALIZED); mMachine.requestState(FINISH); assertEquals(FINISH, mMachine.getState()); } @Test public void testDozeToDozeSuspendTriggers_carModeIsEnabled() { Configuration configuration = configWithCarNightUiMode(); mMachine.onConfigurationChanged(configuration); mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); } @Test public void testDozeAoDToDozeSuspendTriggers_carModeIsEnabled() { Configuration configuration = configWithCarNightUiMode(); mMachine.onConfigurationChanged(configuration); mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE_AOD); assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); } @Test public void testDozePulsingBrightDozeSuspendTriggers_carModeIsEnabled() { Configuration configuration = configWithCarNightUiMode(); mMachine.onConfigurationChanged(configuration); mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE_PULSING_BRIGHT); assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); } @Test public void testDozeAodDockedDozeSuspendTriggers_carModeIsEnabled() { Configuration configuration = configWithCarNightUiMode(); mMachine.onConfigurationChanged(configuration); mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE_AOD_DOCKED); assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); } @Test public void testOnConfigurationChanged_propagatesUiModeTypeToParts() { Configuration newConfig = configWithCarNightUiMode(); mMachine.onConfigurationChanged(newConfig); verify(mPartMock).onUiModeTypeChanged(UI_MODE_TYPE_CAR); verify(mAnotherPartMock).onUiModeTypeChanged(UI_MODE_TYPE_CAR); } @Test public void testOnConfigurationChanged_propagatesOnlyUiModeChangesToParts() { Configuration newConfig = configWithCarNightUiMode(); mMachine.onConfigurationChanged(newConfig); mMachine.onConfigurationChanged(newConfig); verify(mPartMock, times(1)).onUiModeTypeChanged(UI_MODE_TYPE_CAR); verify(mAnotherPartMock, times(1)).onUiModeTypeChanged(UI_MODE_TYPE_CAR); } @Test public void testDozeSuppressTriggers_screenState() { assertEquals(Display.STATE_OFF, DOZE_SUSPEND_TRIGGERS.screenState(null)); } @NonNull private Configuration configWithCarNightUiMode() { Configuration configuration = Configuration.EMPTY; configuration.uiMode = UI_MODE_TYPE_CAR | UI_MODE_NIGHT_YES; return configuration; } }