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