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