1 /*
2  * Copyright (C) 2020 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.classifier;
18 
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.ArgumentMatchers.argThat;
21 import static org.mockito.Mockito.inOrder;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.reset;
24 import static org.mockito.Mockito.times;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.when;
27 
28 import android.testing.TestableLooper;
29 import android.view.KeyEvent;
30 import android.view.MotionEvent;
31 
32 import androidx.test.ext.junit.runners.AndroidJUnit4;
33 import androidx.test.filters.SmallTest;
34 
35 import com.android.keyguard.KeyguardUpdateMonitor;
36 import com.android.systemui.SysuiTestCase;
37 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
38 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
39 import com.android.systemui.dock.DockManager;
40 import com.android.systemui.dock.DockManagerFake;
41 import com.android.systemui.flags.DisableSceneContainer;
42 import com.android.systemui.flags.EnableSceneContainer;
43 import com.android.systemui.plugins.statusbar.StatusBarStateController;
44 import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
45 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
46 import com.android.systemui.statusbar.StatusBarState;
47 import com.android.systemui.statusbar.SysuiStatusBarStateController;
48 import com.android.systemui.statusbar.policy.BatteryController;
49 import com.android.systemui.statusbar.policy.KeyguardStateController;
50 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
51 import com.android.systemui.util.concurrency.FakeExecutor;
52 import com.android.systemui.util.kotlin.JavaAdapter;
53 import com.android.systemui.util.sensors.ProximitySensor;
54 import com.android.systemui.util.sensors.ThresholdSensor;
55 import com.android.systemui.util.time.FakeSystemClock;
56 
57 import kotlinx.coroutines.flow.MutableStateFlow;
58 import kotlinx.coroutines.flow.StateFlowKt;
59 
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.ArgumentCaptor;
64 import org.mockito.InOrder;
65 import org.mockito.Mock;
66 import org.mockito.MockitoAnnotations;
67 
68 @SmallTest
69 @RunWith(AndroidJUnit4.class)
70 @TestableLooper.RunWithLooper(setAsMainLooper = true)
71 public class FalsingCollectorImplTest extends SysuiTestCase {
72 
73     private FalsingCollectorImpl mFalsingCollector;
74     @Mock
75     private FalsingDataProvider mFalsingDataProvider;
76     private final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
77     @Mock
78     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
79     @Mock
80     private HistoryTracker mHistoryTracker;
81     @Mock
82     private ProximitySensor mProximitySensor;
83     @Mock
84     private SysuiStatusBarStateController mStatusBarStateController;
85     @Mock
86     private KeyguardStateController mKeyguardStateController;
87     @Mock
88     private ShadeInteractor mShadeInteractor;
89     @Mock
90     private JavaAdapter mJavaAdapter;
91     @Mock
92     private BatteryController mBatteryController;
93     @Mock
94     private SelectedUserInteractor mSelectedUserInteractor;
95     @Mock
96     private CommunalInteractor mCommunalInteractor;
97     @Mock
98     private DeviceEntryInteractor mDeviceEntryInteractor;
99     private final MutableStateFlow<Boolean> mIsDeviceEntered =
100             StateFlowKt.MutableStateFlow(false);
101     @Mock
102     private SceneContainerOcclusionInteractor mSceneContainerOcclusionInteractor;
103     private final MutableStateFlow<Boolean> mIsInvisibleDueToOcclusion =
104             StateFlowKt.MutableStateFlow(false);
105     private final DockManagerFake mDockManager = new DockManagerFake();
106     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
107     private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
108 
109     @Before
setUp()110     public void setUp() {
111         MockitoAnnotations.initMocks(this);
112 
113         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
114         when(mKeyguardStateController.isShowing()).thenReturn(true);
115         when(mKeyguardStateController.isOccluded()).thenReturn(false);
116         when(mShadeInteractor.isQsExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
117 
118         when(mDeviceEntryInteractor.isDeviceEntered()).thenReturn(mIsDeviceEntered);
119         when(mSceneContainerOcclusionInteractor.getInvisibleDueToOcclusion()).thenReturn(
120                 mIsInvisibleDueToOcclusion);
121 
122         mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
123                 mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
124                 mStatusBarStateController, mKeyguardStateController,
125                 () -> mShadeInteractor, mBatteryController,
126                 mDockManager, mFakeExecutor,
127                 mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor,
128                 () -> mCommunalInteractor, () -> mDeviceEntryInteractor,
129                 () -> mSceneContainerOcclusionInteractor
130         );
131         mFalsingCollector.init();
132     }
133 
134     @Test
testRegisterSensor()135     public void testRegisterSensor() {
136         mFalsingCollector.onScreenTurningOn();
137         verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
138     }
139 
140     @Test
testNoProximityWhenWirelessCharging()141     public void testNoProximityWhenWirelessCharging() {
142         ArgumentCaptor<BatteryController.BatteryStateChangeCallback> batteryCallbackCaptor =
143                 ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
144         verify(mBatteryController).addCallback(batteryCallbackCaptor.capture());
145         batteryCallbackCaptor.getValue().onWirelessChargingChanged(true);
146         verify(mProximitySensor).pause();
147     }
148 
149     @Test
testProximityWhenOffWirelessCharging()150     public void testProximityWhenOffWirelessCharging() {
151         ArgumentCaptor<BatteryController.BatteryStateChangeCallback> batteryCallbackCaptor =
152                 ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
153         verify(mBatteryController).addCallback(batteryCallbackCaptor.capture());
154         batteryCallbackCaptor.getValue().onWirelessChargingChanged(false);
155         verify(mProximitySensor).resume();
156     }
157 
158     @Test
testNoProximityWhenDocked()159     public void testNoProximityWhenDocked() {
160         mDockManager.setDockEvent(DockManager.STATE_DOCKED);
161         verify(mProximitySensor).pause();
162     }
163 
164     @Test
testProximityWhenUndocked()165     public void testProximityWhenUndocked() {
166         mDockManager.setDockEvent(DockManager.STATE_NONE);
167         verify(mProximitySensor).resume();
168     }
169 
170     @Test
testUnregisterSensor()171     public void testUnregisterSensor() {
172         mFalsingCollector.onScreenTurningOn();
173         reset(mProximitySensor);
174         mFalsingCollector.onScreenOff();
175         verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
176     }
177 
178     @Test
testUnregisterSensor_QS()179     public void testUnregisterSensor_QS() {
180         mFalsingCollector.onScreenTurningOn();
181         reset(mProximitySensor);
182         mFalsingCollector.onQsExpansionChanged(true);
183         verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
184         mFalsingCollector.onQsExpansionChanged(false);
185         verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
186     }
187 
188     @Test
testUnregisterSensor_Bouncer()189     public void testUnregisterSensor_Bouncer() {
190         mFalsingCollector.onScreenTurningOn();
191         reset(mProximitySensor);
192         mFalsingCollector.onBouncerShown();
193         verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
194         mFalsingCollector.onBouncerHidden();
195         verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
196     }
197 
198     @Test
testUnregisterSensor_StateTransition()199     public void testUnregisterSensor_StateTransition() {
200         ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
201                 ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
202         verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture());
203 
204         mFalsingCollector.onScreenTurningOn();
205         reset(mProximitySensor);
206         stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE);
207         verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
208     }
209 
210     @Test
211     @DisableSceneContainer
testRegisterSensor_OccludingActivity_sceneContainerDisabled()212     public void testRegisterSensor_OccludingActivity_sceneContainerDisabled() {
213         when(mKeyguardStateController.isOccluded()).thenReturn(true);
214 
215         ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
216                 ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
217         verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture());
218 
219         mFalsingCollector.onScreenTurningOn();
220         reset(mProximitySensor);
221         stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE);
222         verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
223     }
224 
225     @Test
226     @EnableSceneContainer
testRegisterSensor_OccludingActivity_sceneContainerEnabled()227     public void testRegisterSensor_OccludingActivity_sceneContainerEnabled() {
228         mIsInvisibleDueToOcclusion.setValue(true);
229 
230         ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
231                 ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
232         verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture());
233 
234         mFalsingCollector.onScreenTurningOn();
235         reset(mProximitySensor);
236         stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE);
237         verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
238     }
239 
240     @Test
testPassThroughEnterKeyEvent()241     public void testPassThroughEnterKeyEvent() {
242         KeyEvent enterDown = KeyEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER,
243                 0, 0, 0, 0, 0, 0, 0, "");
244         KeyEvent enterUp = KeyEvent.obtain(0, 0, MotionEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0,
245                 0, 0, 0, 0, 0, 0, "");
246 
247         mFalsingCollector.onKeyEvent(enterDown);
248         verify(mFalsingDataProvider, never()).onKeyEvent(any(KeyEvent.class));
249 
250         mFalsingCollector.onKeyEvent(enterUp);
251         verify(mFalsingDataProvider, times(1)).onKeyEvent(enterUp);
252     }
253 
254     @Test
testAvoidAKeyEvent()255     public void testAvoidAKeyEvent() {
256         // Arbitrarily chose the "A" key, as it is not currently allowlisted. If this key is
257         // allowlisted in the future, please choose another key that will not be collected.
258         KeyEvent aKeyDown = KeyEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_A,
259                 0, 0, 0, 0, 0, 0, 0, "");
260         KeyEvent aKeyUp = KeyEvent.obtain(0, 0, MotionEvent.ACTION_UP, KeyEvent.KEYCODE_A, 0,
261                 0, 0, 0, 0, 0, 0, "");
262 
263         mFalsingCollector.onKeyEvent(aKeyDown);
264         verify(mFalsingDataProvider, never()).onKeyEvent(any(KeyEvent.class));
265 
266         mFalsingCollector.onKeyEvent(aKeyUp);
267         verify(mFalsingDataProvider, never()).onKeyEvent(any(KeyEvent.class));
268     }
269 
270     @Test
testPassThroughGesture()271     public void testPassThroughGesture() {
272         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
273         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
274 
275         // Nothing passed initially
276         mFalsingCollector.onTouchEvent(down);
277         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
278 
279         // Up event flushes the down event.
280         mFalsingCollector.onTouchEvent(up);
281         InOrder orderedCalls = inOrder(mFalsingDataProvider);
282         // We can't simply use "eq" or similar because the collector makes a copy of "down".
283         orderedCalls.verify(mFalsingDataProvider).onMotionEvent(
284                 argThat(argument -> argument.getActionMasked() == MotionEvent.ACTION_DOWN));
285         orderedCalls.verify(mFalsingDataProvider).onMotionEvent(up);
286     }
287 
288     @Test
testAvoidGesture()289     public void testAvoidGesture() {
290         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
291         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
292 
293         // Nothing passed initially
294         mFalsingCollector.onTouchEvent(down);
295         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
296 
297         mFalsingCollector.avoidGesture();
298         // Up event would flush, but we were told to avoid.
299         mFalsingCollector.onTouchEvent(up);
300         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
301     }
302 
303     @Test
testIgnoreActionOutside()304     public void testIgnoreActionOutside() {
305         MotionEvent outside = MotionEvent.obtain(0, 0, MotionEvent.ACTION_OUTSIDE, 0, 0, 0);
306         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
307 
308         // Nothing passed initially. The outside event will be completely ignored.
309         mFalsingCollector.onTouchEvent(outside);
310         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
311 
312         // Up event flushes, and the outside event isn't passed through.
313         mFalsingCollector.onTouchEvent(up);
314         verify(mFalsingDataProvider).onMotionEvent(up);
315     }
316 
317     @Test
318     @DisableSceneContainer
testAvoidUnlocked_sceneContainerDisabled()319     public void testAvoidUnlocked_sceneContainerDisabled() {
320         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
321         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
322 
323         when(mKeyguardStateController.isShowing()).thenReturn(false);
324 
325         // Nothing passed initially
326         mFalsingCollector.onTouchEvent(down);
327         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
328 
329         // Up event would normally flush the up event, but doesn't.
330         mFalsingCollector.onTouchEvent(up);
331         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
332     }
333 
334     @Test
335     @EnableSceneContainer
testAvoidUnlocked_sceneContainerEnabled()336     public void testAvoidUnlocked_sceneContainerEnabled() {
337         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
338         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
339 
340         mIsDeviceEntered.setValue(true);
341 
342         // Nothing passed initially
343         mFalsingCollector.onTouchEvent(down);
344         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
345 
346         // Up event would normally flush the up event, but doesn't.
347         mFalsingCollector.onTouchEvent(up);
348         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
349     }
350 
351     @Test
testGestureWhenDozing()352     public void testGestureWhenDozing() {
353         // We check the FalsingManager for taps during the transition to AoD (dozing=true,
354         // pulsing=false), so the FalsingCollector needs to continue to analyze events that occur
355         // while the device is dozing.
356         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
357         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
358 
359         when(mStatusBarStateController.isDozing()).thenReturn(true);
360 
361         // Nothing passed initially
362         mFalsingCollector.onTouchEvent(down);
363         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
364 
365         // Up event flushes
366         mFalsingCollector.onTouchEvent(up);
367         verify(mFalsingDataProvider, times(2)).onMotionEvent(any(MotionEvent.class));
368     }
369 
370     @Test
testGestureWhenPulsing()371     public void testGestureWhenPulsing() {
372         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
373         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
374 
375         when(mStatusBarStateController.isDozing()).thenReturn(true);
376         when(mStatusBarStateController.isPulsing()).thenReturn(true);
377 
378         // Nothing passed initially
379         mFalsingCollector.onTouchEvent(down);
380         verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
381 
382         // Up event would flushes
383         mFalsingCollector.onTouchEvent(up);
384         verify(mFalsingDataProvider, times(2)).onMotionEvent(any(MotionEvent.class));
385     }
386 
387     @Test
testOnA11yAction()388     public void testOnA11yAction() {
389         mFalsingCollector.onA11yAction();
390         verify(mFalsingDataProvider).onA11yAction();
391     }
392 }
393