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 com.android.systemui.plugins.FalsingManager.HIGH_PENALTY; 20 import static com.android.systemui.plugins.FalsingManager.NO_PENALTY; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyCollection; 26 import static org.mockito.ArgumentMatchers.anyDouble; 27 import static org.mockito.ArgumentMatchers.anyInt; 28 import static org.mockito.ArgumentMatchers.eq; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 32 import android.testing.AndroidTestingRunner; 33 import android.view.MotionEvent; 34 import android.view.accessibility.AccessibilityManager; 35 36 import androidx.test.filters.SmallTest; 37 38 import com.android.internal.logging.MetricsLogger; 39 import com.android.internal.logging.testing.FakeMetricsLogger; 40 import com.android.systemui.SysuiTestCase; 41 import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener; 42 import com.android.systemui.flags.FakeFeatureFlags; 43 import com.android.systemui.flags.Flags; 44 import com.android.systemui.statusbar.policy.KeyguardStateController; 45 import com.android.systemui.util.concurrency.FakeExecutor; 46 import com.android.systemui.util.time.FakeSystemClock; 47 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.ArgumentCaptor; 52 import org.mockito.Mock; 53 import org.mockito.MockitoAnnotations; 54 55 import java.util.ArrayList; 56 import java.util.HashSet; 57 import java.util.List; 58 import java.util.Set; 59 60 @SmallTest 61 @RunWith(AndroidTestingRunner.class) 62 public class BrightLineClassifierTest extends SysuiTestCase { 63 private BrightLineFalsingManager mBrightLineFalsingManager; 64 @Mock 65 private FalsingDataProvider mFalsingDataProvider; 66 private final MetricsLogger mMetricsLogger = new FakeMetricsLogger(); 67 private final Set<FalsingClassifier> mClassifiers = new HashSet<>(); 68 @Mock 69 private SingleTapClassifier mSingleTapClassfier; 70 @Mock 71 private LongTapClassifier mLongTapClassifier; 72 @Mock 73 private DoubleTapClassifier mDoubleTapClassifier; 74 @Mock 75 private FalsingClassifier mClassifierA; 76 @Mock 77 private FalsingClassifier mClassifierB; 78 private final List<MotionEvent> mMotionEventList = new ArrayList<>(); 79 @Mock 80 private HistoryTracker mHistoryTracker; 81 @Mock 82 private KeyguardStateController mKeyguardStateController; 83 @Mock 84 private AccessibilityManager mAccessibilityManager; 85 86 private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); 87 private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags(); 88 89 private final FalsingClassifier.Result mFalsedResult = 90 FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), ""); 91 private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1); 92 private GestureFinalizedListener mGestureFinalizedListener; 93 94 @Before setup()95 public void setup() { 96 MockitoAnnotations.initMocks(this); 97 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 98 .thenReturn(mPassedResult); 99 when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble())) 100 .thenReturn(mPassedResult); 101 when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mPassedResult); 102 when(mLongTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult); 103 when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) 104 .thenReturn(mPassedResult); 105 mClassifiers.add(mClassifierA); 106 mClassifiers.add(mClassifierB); 107 when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList); 108 when(mKeyguardStateController.isShowing()).thenReturn(true); 109 when(mFalsingDataProvider.isUnfolded()).thenReturn(false); 110 mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, 111 mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier, 112 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController, 113 mAccessibilityManager, false, mFakeFeatureFlags); 114 115 116 ArgumentCaptor<GestureFinalizedListener> gestureCompleteListenerCaptor = 117 ArgumentCaptor.forClass(GestureFinalizedListener.class); 118 119 verify(mFalsingDataProvider).addGestureCompleteListener( 120 gestureCompleteListenerCaptor.capture()); 121 122 mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue(); 123 mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true); 124 } 125 126 @Test testRegisterSessionListener()127 public void testRegisterSessionListener() { 128 verify(mFalsingDataProvider).addSessionListener( 129 any(FalsingDataProvider.SessionListener.class)); 130 131 mBrightLineFalsingManager.cleanupInternal(); 132 verify(mFalsingDataProvider).removeSessionListener( 133 any(FalsingDataProvider.SessionListener.class)); 134 } 135 136 @Test testIsFalseTouch_NoClassifiers()137 public void testIsFalseTouch_NoClassifiers() { 138 mClassifiers.clear(); 139 140 assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); 141 } 142 143 @Test testIsFalseTouch_ClassifiersPass()144 public void testIsFalseTouch_ClassifiersPass() { 145 assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); 146 } 147 148 @Test testIsFalseTouch_ClassifierARejects()149 public void testIsFalseTouch_ClassifierARejects() { 150 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 151 .thenReturn(mFalsedResult); 152 assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); 153 } 154 155 @Test testIsFalseTouch_SeekBar_FalseTouch()156 public void testIsFalseTouch_SeekBar_FalseTouch() { 157 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 158 .thenReturn(mFalsedResult); 159 when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult); 160 assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).isTrue(); 161 } 162 163 @Test testIsFalseTouch_SeekBar_RealTouch()164 public void testIsFalseTouch_SeekBar_RealTouch() { 165 when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult); 166 assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).isFalse(); 167 } 168 169 @Test testIsFalseTouch_SeekBar_FalseTap()170 public void testIsFalseTouch_SeekBar_FalseTap() { 171 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 172 .thenReturn(mFalsedResult); 173 when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult); 174 assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).isTrue(); 175 } 176 177 @Test testIsFalseTouch_SeekBar_RealTap()178 public void testIsFalseTouch_SeekBar_RealTap() { 179 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 180 .thenReturn(mFalsedResult); 181 assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).isFalse(); 182 } 183 184 @Test testIsFalseTouch_ClassifierBRejects()185 public void testIsFalseTouch_ClassifierBRejects() { 186 when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble())) 187 .thenReturn(mFalsedResult); 188 assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); 189 } 190 191 @Test testIsFalseTouch_FaceAuth()192 public void testIsFalseTouch_FaceAuth() { 193 // Even when the classifiers report a false, we should allow. 194 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 195 .thenReturn(mPassedResult); 196 when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); 197 198 assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); 199 } 200 201 @Test testIsFalseTouch_Docked()202 public void testIsFalseTouch_Docked() { 203 // Even when the classifiers report a false, we should allow. 204 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 205 .thenReturn(mPassedResult); 206 when(mFalsingDataProvider.isDocked()).thenReturn(true); 207 208 assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); 209 } 210 211 @Test testIsFalseTap_BasicCheck()212 public void testIsFalseTap_BasicCheck() { 213 when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult); 214 215 assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse(); 216 217 when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult); 218 219 assertThat(mBrightLineFalsingManager.isSimpleTap()).isTrue(); 220 } 221 222 @Test testIsFalseSingleTap_EmptyRecentEvents()223 public void testIsFalseSingleTap_EmptyRecentEvents() { 224 // Ensure we look at prior events if recent events has already been emptied. 225 when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>()); 226 when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList); 227 228 mBrightLineFalsingManager.isFalseTap(0); 229 verify(mSingleTapClassfier).isTap(mMotionEventList, 0); 230 } 231 232 233 @Test testIsFalseSingleTap_RobustCheck_NoFaceAuth()234 public void testIsFalseSingleTap_RobustCheck_NoFaceAuth() { 235 when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult); 236 when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) 237 .thenReturn(mFalsedResult); 238 when(mHistoryTracker.falseBelief()).thenReturn(1.0); 239 mFalsingDataProvider.setJustUnlockedWithFace(false); 240 assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isTrue(); 241 } 242 243 @Test testIsFalseSingleTap_RobustCheck_FaceAuth()244 public void testIsFalseSingleTap_RobustCheck_FaceAuth() { 245 when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult); 246 when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); 247 assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isFalse(); 248 } 249 250 @Test testIsFalseLongTap_EmptyRecentEvents()251 public void testIsFalseLongTap_EmptyRecentEvents() { 252 // Ensure we look at prior events if recent events has already been emptied. 253 when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>()); 254 when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList); 255 256 mBrightLineFalsingManager.isFalseLongTap(0); 257 verify(mLongTapClassifier).isTap(mMotionEventList, 0); 258 } 259 260 @Test testIsFalseLongTap_FalseLongTap()261 public void testIsFalseLongTap_FalseLongTap() { 262 when(mLongTapClassifier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult); 263 assertThat(mBrightLineFalsingManager.isFalseLongTap(NO_PENALTY)).isTrue(); 264 } 265 266 @Test testIsFalseLongTap_RobustCheck_NoFaceAuth()267 public void testIsFalseLongTap_RobustCheck_NoFaceAuth() { 268 when(mLongTapClassifier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult); 269 when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(false); 270 assertThat(mBrightLineFalsingManager.isFalseLongTap(NO_PENALTY)).isFalse(); 271 } 272 273 @Test testIsFalseLongTap_RobustCheck_FaceAuth()274 public void testIsFalseLongTap_RobustCheck_FaceAuth() { 275 when(mLongTapClassifier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult); 276 when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); 277 assertThat(mBrightLineFalsingManager.isFalseLongTap(NO_PENALTY)).isFalse(); 278 } 279 280 @Test testIsFalseDoubleTap()281 public void testIsFalseDoubleTap() { 282 when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) 283 .thenReturn(mPassedResult); 284 285 assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); 286 287 when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) 288 .thenReturn(mFalsedResult); 289 290 assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue(); 291 } 292 293 @Test testHistory()294 public void testHistory() { 295 mGestureFinalizedListener.onGestureFinalized(1000); 296 297 verify(mHistoryTracker).addResults(anyCollection(), eq(1000L)); 298 } 299 300 @Test testHistory_singleTap()301 public void testHistory_singleTap() { 302 // When trying to classify single taps, we don't immediately add results to history. 303 mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY); 304 mGestureFinalizedListener.onGestureFinalized(1000); 305 verify(mHistoryTracker).addResults(anyCollection(), eq(1000L)); 306 } 307 308 @Test testHistory_multipleSingleTaps()309 public void testHistory_multipleSingleTaps() { 310 // When trying to classify single taps, we don't immediately add results to history. 311 mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY); 312 mGestureFinalizedListener.onGestureFinalized(1000); 313 mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY); 314 mGestureFinalizedListener.onGestureFinalized(2000); 315 verify(mHistoryTracker).addResults(anyCollection(), eq(1000L)); 316 verify(mHistoryTracker).addResults(anyCollection(), eq(2000L)); 317 } 318 319 @Test testHistory_doubleTap()320 public void testHistory_doubleTap() { 321 // When trying to classify single taps, we don't immediately add results to history. 322 mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY); 323 mGestureFinalizedListener.onGestureFinalized(1000); 324 // Before checking for double tap, we may check for single-tap on the second gesture. 325 mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY); 326 mBrightLineFalsingManager.isFalseDoubleTap(); 327 mGestureFinalizedListener.onGestureFinalized(2000); 328 329 // Double tap is immediately added to history. Single tap is never added. 330 verify(mHistoryTracker).addResults(anyCollection(), eq(2000L)); 331 332 assertThat(mFakeExecutor.numPending()).isEqualTo(0); 333 } 334 335 @Test testNoFalsingUnlocked()336 public void testNoFalsingUnlocked() { 337 when(mKeyguardStateController.isShowing()).thenReturn(false); 338 339 when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) 340 .thenReturn(mFalsedResult); 341 assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); 342 343 when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult); 344 assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse(); 345 346 when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) 347 .thenReturn(mFalsedResult); 348 assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); 349 } 350 } 351