1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.doze; 18 19 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; 20 import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; 21 import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyFloat; 25 import static org.mockito.ArgumentMatchers.anyInt; 26 import static org.mockito.ArgumentMatchers.anyString; 27 import static org.mockito.ArgumentMatchers.eq; 28 import static org.mockito.Mockito.clearInvocations; 29 import static org.mockito.Mockito.doAnswer; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.spy; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.when; 34 35 import android.app.StatusBarManager; 36 import android.hardware.Sensor; 37 import android.hardware.display.AmbientDisplayConfiguration; 38 import android.testing.AndroidTestingRunner; 39 import android.testing.TestableLooper.RunWithLooper; 40 import android.view.Display; 41 42 import androidx.test.filters.SmallTest; 43 44 import com.android.internal.logging.InstanceId; 45 import com.android.internal.logging.UiEventLogger; 46 import com.android.systemui.SysuiTestCase; 47 import com.android.systemui.biometrics.AuthController; 48 import com.android.systemui.broadcast.BroadcastDispatcher; 49 import com.android.systemui.dock.DockManager; 50 import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent; 51 import com.android.systemui.log.SessionTracker; 52 import com.android.systemui.settings.UserTracker; 53 import com.android.systemui.statusbar.phone.DozeParameters; 54 import com.android.systemui.statusbar.policy.DevicePostureController; 55 import com.android.systemui.statusbar.policy.KeyguardStateController; 56 import com.android.systemui.util.concurrency.FakeExecutor; 57 import com.android.systemui.util.concurrency.FakeThreadFactory; 58 import com.android.systemui.util.sensors.AsyncSensorManager; 59 import com.android.systemui.util.sensors.FakeProximitySensor; 60 import com.android.systemui.util.sensors.FakeSensorManager; 61 import com.android.systemui.util.sensors.FakeThresholdSensor; 62 import com.android.systemui.util.sensors.ProximityCheck; 63 import com.android.systemui.util.sensors.ThresholdSensorEvent; 64 import com.android.systemui.util.settings.FakeSettings; 65 import com.android.systemui.util.time.FakeSystemClock; 66 import com.android.systemui.util.wakelock.WakeLock; 67 import com.android.systemui.util.wakelock.WakeLockFake; 68 69 import org.junit.Before; 70 import org.junit.Test; 71 import org.junit.runner.RunWith; 72 import org.mockito.ArgumentCaptor; 73 import org.mockito.Mock; 74 import org.mockito.MockitoAnnotations; 75 76 @SmallTest 77 @RunWith(AndroidTestingRunner.class) 78 @RunWithLooper(setAsMainLooper = true) 79 public class DozeTriggersTest extends SysuiTestCase { 80 81 @Mock 82 private DozeMachine mMachine; 83 @Mock 84 private DozeHost mHost; 85 @Mock 86 private BroadcastDispatcher mBroadcastDispatcher; 87 @Mock 88 private DockManager mDockManager; 89 @Mock 90 private ProximityCheck mProximityCheck; 91 @Mock 92 private DozeLog mDozeLog; 93 @Mock 94 private AuthController mAuthController; 95 @Mock 96 private UiEventLogger mUiEventLogger; 97 @Mock 98 private KeyguardStateController mKeyguardStateController; 99 @Mock 100 private DevicePostureController mDevicePostureController; 101 @Mock 102 private UserTracker mUserTracker; 103 @Mock 104 private SessionTracker mSessionTracker; 105 106 private DozeTriggers mTriggers; 107 private FakeSensorManager mSensors; 108 private Sensor mTapSensor; 109 private FakeProximitySensor mProximitySensor; 110 private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); 111 112 @Before setUp()113 public void setUp() throws Exception { 114 MockitoAnnotations.initMocks(this); 115 setupDozeTriggers( 116 DozeConfigurationUtil.createMockConfig(), 117 DozeConfigurationUtil.createMockParameters()); 118 } 119 setupDozeTriggers( AmbientDisplayConfiguration config, DozeParameters dozeParameters)120 private void setupDozeTriggers( 121 AmbientDisplayConfiguration config, 122 DozeParameters dozeParameters) throws Exception { 123 mSensors = spy(new FakeSensorManager(mContext)); 124 mTapSensor = mSensors.getFakeTapSensor().getSensor(); 125 WakeLock wakeLock = new WakeLockFake(); 126 AsyncSensorManager asyncSensorManager = 127 new AsyncSensorManager(mSensors, new FakeThreadFactory(mExecutor), null); 128 129 FakeThresholdSensor thresholdSensor = new FakeThresholdSensor(); 130 thresholdSensor.setLoaded(true); 131 mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor); 132 133 mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters, 134 asyncSensorManager, wakeLock, mDockManager, mProximitySensor, 135 mProximityCheck, mDozeLog, mBroadcastDispatcher, new FakeSettings(), 136 mAuthController, mUiEventLogger, mSessionTracker, mKeyguardStateController, 137 mDevicePostureController, mUserTracker); 138 mTriggers.setDozeMachine(mMachine); 139 waitForSensorManager(); 140 } 141 142 @Test testOnNotification_stillWorksAfterOneFailedProxCheck()143 public void testOnNotification_stillWorksAfterOneFailedProxCheck() { 144 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); 145 ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class); 146 doAnswer(invocation -> null).when(mHost).addCallback(captor.capture()); 147 148 mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); 149 mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); 150 clearInvocations(mMachine); 151 152 ArgumentCaptor<Boolean> boolCaptor = ArgumentCaptor.forClass(Boolean.class); 153 doAnswer(invocation -> 154 when(mHost.isPulsePending()).thenReturn(boolCaptor.getValue()) 155 ).when(mHost).setPulsePending(boolCaptor.capture()); 156 157 when(mHost.isPulsingBlocked()).thenReturn(false); 158 mProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 1)); 159 captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); 160 mProximitySensor.alertListeners(); 161 162 verify(mMachine, never()).requestState(any()); 163 verify(mMachine, never()).requestPulse(anyInt()); 164 165 mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2)); 166 mProximitySensor.alertListeners(); 167 waitForSensorManager(); 168 captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); 169 170 verify(mMachine).requestPulse(anyInt()); 171 } 172 173 @Test testOnNotification_noPulseIfPulseIsNotPendingAnymore()174 public void testOnNotification_noPulseIfPulseIsNotPendingAnymore() { 175 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); 176 ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class); 177 doAnswer(invocation -> null).when(mHost).addCallback(captor.capture()); 178 179 mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); 180 mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); 181 clearInvocations(mMachine); 182 when(mHost.isPulsingBlocked()).thenReturn(false); 183 184 // GIVEN pulsePending = false 185 when(mHost.isPulsePending()).thenReturn(false); 186 187 // WHEN prox check returns FAR 188 mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2)); 189 captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); 190 mProximitySensor.alertListeners(); 191 192 // THEN don't request pulse because the pending pulse was abandoned early 193 verify(mMachine, never()).requestPulse(anyInt()); 194 } 195 196 @Test testTransitionTo_disablesAndEnablesTouchSensors()197 public void testTransitionTo_disablesAndEnablesTouchSensors() { 198 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); 199 200 mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); 201 mTriggers.onScreenState(Display.STATE_OFF); 202 waitForSensorManager(); 203 verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor)); 204 205 clearInvocations(mSensors); 206 mTriggers.transitionTo(DozeMachine.State.DOZE, 207 DozeMachine.State.DOZE_REQUEST_PULSE); 208 mTriggers.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE, 209 DozeMachine.State.DOZE_PULSING); 210 mTriggers.onScreenState(Display.STATE_DOZE); 211 waitForSensorManager(); 212 verify(mSensors).cancelTriggerSensor(any(), eq(mTapSensor)); 213 214 clearInvocations(mSensors); 215 mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE); 216 mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DOZE_AOD); 217 waitForSensorManager(); 218 verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor)); 219 } 220 221 @Test transitionToDockedAod_disablesTouchSensors()222 public void transitionToDockedAod_disablesTouchSensors() { 223 mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); 224 mTriggers.onScreenState(Display.STATE_OFF); 225 waitForSensorManager(); 226 verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor)); 227 228 mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.DOZE_AOD_DOCKED); 229 mTriggers.onScreenState(Display.STATE_DOZE); 230 waitForSensorManager(); 231 232 verify(mSensors).cancelTriggerSensor(any(), eq(mTapSensor)); 233 } 234 235 @Test transitionToDozeSuspendTriggers_disablesAllCallbacks()236 public void transitionToDozeSuspendTriggers_disablesAllCallbacks() { 237 mTriggers.transitionTo(UNINITIALIZED, INITIALIZED); 238 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_SUSPEND_TRIGGERS); 239 240 mTriggers.transitionTo(DozeMachine.State.INITIALIZED, 241 DozeMachine.State.DOZE_SUSPEND_TRIGGERS); 242 243 verify(mDockManager).removeListener(any()); 244 verify(mBroadcastDispatcher).unregisterReceiver(any()); 245 verify(mHost).removeCallback(any()); 246 } 247 248 @Test testDockEventListener_registerAndUnregister()249 public void testDockEventListener_registerAndUnregister() { 250 mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); 251 verify(mDockManager).addListener(any()); 252 253 mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH); 254 verify(mDockManager).removeListener(any()); 255 } 256 257 @Test testProximitySensorNotAvailable()258 public void testProximitySensorNotAvailable() { 259 mProximitySensor.setSensorAvailable(false); 260 mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null); 261 mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_WAKE_REACH, 100, 100, 262 new float[]{1}); 263 mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 100, null); 264 } 265 266 @Test testQuickPickup()267 public void testQuickPickup() { 268 // GIVEN device is in doze (screen blank, but running doze sensors) 269 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); 270 InstanceId keyguardSessionId = InstanceId.fakeInstanceId(99); 271 when(mSessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)) 272 .thenReturn(keyguardSessionId); 273 274 ArgumentCaptor<Boolean> boolCaptor = ArgumentCaptor.forClass(Boolean.class); 275 doAnswer(invocation -> 276 when(mHost.isPulsePending()).thenReturn(boolCaptor.getValue()) 277 ).when(mHost).setPulsePending(boolCaptor.capture()); 278 279 // WHEN quick pick up is triggered 280 mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null); 281 282 // THEN request pulse 283 verify(mMachine).requestPulse(anyInt()); 284 285 // THEN a log is taken that quick pick up was triggered 286 verify(mUiEventLogger) 287 .log(DozingUpdateUiEvent.DOZING_UPDATE_QUICK_PICKUP, keyguardSessionId); 288 } 289 290 @Test testPickupGesture()291 public void testPickupGesture() { 292 // GIVEN device is in doze (screen blank, but running doze sensors) 293 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); 294 295 // WHEN the pick up gesture is triggered and keyguard isn't occluded 296 when(mKeyguardStateController.isOccluded()).thenReturn(false); 297 mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); 298 299 // THEN wakeup 300 verify(mMachine).wakeUp(DozeLog.REASON_SENSOR_PICKUP); 301 } 302 303 @Test test_onSensor_tap()304 public void test_onSensor_tap() { 305 mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 200, null); 306 307 verify(mHost).onSlpiTap(100, 200); 308 verify(mMachine).wakeUp(DozeLog.REASON_SENSOR_TAP); 309 } 310 311 @Test test_onSensor_double_tap()312 public void test_onSensor_double_tap() { 313 mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP, 100, 200, null); 314 315 verify(mHost).onSlpiTap(100, 200); 316 verify(mMachine).wakeUp(DozeLog.REASON_SENSOR_DOUBLE_TAP); 317 } 318 319 @Test testPickupGestureDroppedKeyguardOccluded()320 public void testPickupGestureDroppedKeyguardOccluded() { 321 // GIVEN device is in doze (screen blank, but running doze sensors) 322 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); 323 324 // WHEN the pick up gesture is triggered and keyguard IS occluded 325 when(mKeyguardStateController.isOccluded()).thenReturn(true); 326 mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); 327 328 // THEN never wakeup 329 verify(mMachine, never()).wakeUp(DozeLog.REASON_SENSOR_PICKUP); 330 } 331 332 @Test testOnSensor_Fingerprint()333 public void testOnSensor_Fingerprint() { 334 // GIVEN dozing state 335 when(mMachine.getState()).thenReturn(DOZE_AOD); 336 final int screenX = 100; 337 final int screenY = 100; 338 final float misc = -1; 339 final float minor = 2f; 340 final float major = 3f; 341 final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; 342 float[] rawValues = new float[]{screenX, screenY, misc, major, minor}; 343 344 // WHEN longpress gesture is triggered 345 mTriggers.onSensor(reason, screenX, screenY, rawValues); 346 347 // THEN 348 // * don't immediately send interrupt 349 // * immediately extend pulse 350 verify(mAuthController, never()).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); 351 verify(mHost).extendPulse(reason); 352 353 // WHEN display state changes to ON 354 mTriggers.onScreenState(Display.STATE_ON); 355 356 // THEN send interrupt 357 verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(major), eq(minor)); 358 } 359 360 @Test testDestroy()361 public void testDestroy() { 362 mTriggers.destroy(); 363 verify(mProximityCheck).destroy(); 364 } 365 366 @Test testIsExecutingTransition_dropPulse()367 public void testIsExecutingTransition_dropPulse() { 368 when(mHost.isPulsePending()).thenReturn(false); 369 when(mMachine.isExecutingTransition()).thenReturn(true); 370 371 mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null); 372 373 verify(mDozeLog).tracePulseDropped(anyString(), eq(null)); 374 } 375 376 @Test udfpsLongPress_triggeredWhenAodPaused()377 public void udfpsLongPress_triggeredWhenAodPaused() { 378 // GIVEN device is DOZE_AOD_PAUSED 379 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_AOD_PAUSED); 380 381 // WHEN udfps long-press is triggered 382 mTriggers.onSensor(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, 100, 100, 383 new float[]{0, 1, 2, 3, 4}); 384 385 // THEN the pulse is NOT dropped 386 verify(mDozeLog, never()).tracePulseDropped(anyString(), any()); 387 388 // WHEN the screen state is ON 389 mTriggers.onScreenState(Display.STATE_ON); 390 391 // THEN aod interrupt is sent 392 verify(mAuthController).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); 393 } 394 395 @Test udfpsLongPress_triggeredWhenAodPausing()396 public void udfpsLongPress_triggeredWhenAodPausing() { 397 // GIVEN device is DOZE_AOD_PAUSED 398 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_AOD_PAUSING); 399 400 // WHEN udfps long-press is triggered 401 mTriggers.onSensor(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, 100, 100, 402 new float[]{0, 1, 2, 3, 4}); 403 404 // THEN the pulse is NOT dropped 405 verify(mDozeLog, never()).tracePulseDropped(anyString(), any()); 406 407 // WHEN the screen state is ON 408 mTriggers.onScreenState(Display.STATE_ON); 409 410 // THEN aod interrupt is sent 411 verify(mAuthController).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); 412 } 413 414 415 @Test udfpsLongPress_dozeState_notRegistered()416 public void udfpsLongPress_dozeState_notRegistered() { 417 // GIVEN device is DOZE_AOD_PAUSED 418 when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); 419 // beverlyt 420 } 421 waitForSensorManager()422 private void waitForSensorManager() { 423 mExecutor.runAllReady(); 424 } 425 } 426