Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +35 −4 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; Loading Loading @@ -106,6 +107,7 @@ public class UdfpsController implements DozeReceiver { private final DelayableExecutor mFgExecutor; @NonNull private final StatusBar mStatusBar; @NonNull private final StatusBarStateController mStatusBarStateController; @NonNull private final KeyguardStateController mKeyguardStateController; @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; @NonNull private final DumpManager mDumpManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; Loading Loading @@ -148,6 +150,7 @@ public class UdfpsController implements DozeReceiver { private boolean mScreenOn; private Runnable mAodInterruptRunnable; private boolean mOnFingerDown; private boolean mAttemptedToDismissKeyguard; @VisibleForTesting public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = Loading Loading @@ -379,8 +382,12 @@ public class UdfpsController implements DozeReceiver { // ACTION_DOWN, in that case we should just reuse the old instance. mVelocityTracker.clear(); } if (isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView)) { boolean withinSensorArea = isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); if (withinSensorArea) { Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); Log.v(TAG, "onTouch | action down"); // The pointer that causes ACTION_DOWN is always at index 0. // We need to persist its ID to track it during ACTION_MOVE that could include // data for many other pointers because of multi-touch support. Loading @@ -388,6 +395,11 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker.addMovement(event); handled = true; } if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) { Log.v(TAG, "onTouch | dismiss keyguard from ACTION_DOWN"); mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */); mAttemptedToDismissKeyguard = true; } Trace.endSection(); break; Loading @@ -398,8 +410,10 @@ public class UdfpsController implements DozeReceiver { ? event.getPointerId(0) : event.findPointerIndex(mActivePointerId); if (idx == event.getActionIndex()) { if (isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), fromUdfpsView)) { boolean actionMoveWithinSensorArea = isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), fromUdfpsView); if (actionMoveWithinSensorArea) { if (mVelocityTracker == null) { // touches could be injected, so the velocity tracker may not have // been initialized (via ACTION_DOWN). Loading Loading @@ -434,6 +448,12 @@ public class UdfpsController implements DozeReceiver { Log.v(TAG, "onTouch | finger outside"); onFingerUp(); } if ((fromUdfpsView || actionMoveWithinSensorArea) && shouldTryToDismissKeyguard()) { Log.v(TAG, "onTouch | dismiss keyguard from ACTION_MOVE"); mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */); mAttemptedToDismissKeyguard = true; } } Trace.endSection(); break; Loading @@ -448,6 +468,7 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker = null; } Log.v(TAG, "onTouch | finger up"); mAttemptedToDismissKeyguard = false; onFingerUp(); mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION); Trace.endSection(); Loading @@ -459,6 +480,13 @@ public class UdfpsController implements DozeReceiver { return handled; } private boolean shouldTryToDismissKeyguard() { return mView.getAnimationViewController() != null && mView.getAnimationViewController() instanceof UdfpsKeyguardViewController && mKeyguardStateController.canDismissLockScreen() && !mAttemptedToDismissKeyguard; } @Inject public UdfpsController(@NonNull Context context, @NonNull Execution execution, Loading @@ -479,7 +507,8 @@ public class UdfpsController implements DozeReceiver { @NonNull ScreenLifecycle screenLifecycle, @Nullable Vibrator vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @NonNull Optional<UdfpsHbmProvider> hbmProvider) { @NonNull Optional<UdfpsHbmProvider> hbmProvider, @NonNull KeyguardStateController keyguardStateController) { mContext = context; mExecution = execution; // TODO (b/185124905): inject main handler and vibrator once done prototyping Loading @@ -493,6 +522,7 @@ public class UdfpsController implements DozeReceiver { mFgExecutor = fgExecutor; mStatusBar = statusBar; mStatusBarStateController = statusBarStateController; mKeyguardStateController = keyguardStateController; mKeyguardViewManager = statusBarKeyguardViewManager; mDumpManager = dumpManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; Loading Loading @@ -667,6 +697,7 @@ public class UdfpsController implements DozeReceiver { mView.setSensorProperties(mSensorProps); mView.setHbmProvider(mHbmProvider); UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); mAttemptedToDismissKeyguard = false; animation.init(); mView.setAnimationViewController(animation); mOrientationListener.enable(); Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +80 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; Loading Loading @@ -58,6 +59,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; import com.android.systemui.util.concurrency.FakeExecutor; Loading Loading @@ -130,6 +132,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private Vibrator mVibrator; @Mock private UdfpsHapticsSimulator mUdfpsHapticsSimulator; @Mock private KeyguardStateController mKeyguardStateController; private FakeExecutor mFgExecutor; Loading @@ -137,6 +141,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private UdfpsView mUdfpsView; @Mock private UdfpsKeyguardViewController mUdfpsKeyguardViewController; @Mock private TypedArray mBrightnessValues; @Mock private TypedArray mBrightnessBacklight; Loading @@ -149,6 +155,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor; private ScreenLifecycle.Observer mScreenObserver; @Captor private ArgumentCaptor<UdfpsAnimationViewController> mAnimViewControllerCaptor; @Before public void setUp() { setUpResources(); Loading Loading @@ -193,7 +201,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mScreenLifecycle, mVibrator, mUdfpsHapticsSimulator, Optional.of(mHbmProvider)); Optional.of(mHbmProvider), mKeyguardStateController); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); Loading @@ -220,6 +229,76 @@ public class UdfpsControllerTest extends SysuiTestCase { verify(mUdfpsView).dozeTimeTick(); } @Test public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_DOWN is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); downEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); } @Test public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_MOVE is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); } @Test public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN multiple touches are received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); downEvent.recycle(); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); } @Test public void showUdfpsOverlay_addsViewToWindow() throws RemoteException { mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +35 −4 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; Loading Loading @@ -106,6 +107,7 @@ public class UdfpsController implements DozeReceiver { private final DelayableExecutor mFgExecutor; @NonNull private final StatusBar mStatusBar; @NonNull private final StatusBarStateController mStatusBarStateController; @NonNull private final KeyguardStateController mKeyguardStateController; @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; @NonNull private final DumpManager mDumpManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; Loading Loading @@ -148,6 +150,7 @@ public class UdfpsController implements DozeReceiver { private boolean mScreenOn; private Runnable mAodInterruptRunnable; private boolean mOnFingerDown; private boolean mAttemptedToDismissKeyguard; @VisibleForTesting public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = Loading Loading @@ -379,8 +382,12 @@ public class UdfpsController implements DozeReceiver { // ACTION_DOWN, in that case we should just reuse the old instance. mVelocityTracker.clear(); } if (isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView)) { boolean withinSensorArea = isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); if (withinSensorArea) { Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); Log.v(TAG, "onTouch | action down"); // The pointer that causes ACTION_DOWN is always at index 0. // We need to persist its ID to track it during ACTION_MOVE that could include // data for many other pointers because of multi-touch support. Loading @@ -388,6 +395,11 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker.addMovement(event); handled = true; } if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) { Log.v(TAG, "onTouch | dismiss keyguard from ACTION_DOWN"); mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */); mAttemptedToDismissKeyguard = true; } Trace.endSection(); break; Loading @@ -398,8 +410,10 @@ public class UdfpsController implements DozeReceiver { ? event.getPointerId(0) : event.findPointerIndex(mActivePointerId); if (idx == event.getActionIndex()) { if (isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), fromUdfpsView)) { boolean actionMoveWithinSensorArea = isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), fromUdfpsView); if (actionMoveWithinSensorArea) { if (mVelocityTracker == null) { // touches could be injected, so the velocity tracker may not have // been initialized (via ACTION_DOWN). Loading Loading @@ -434,6 +448,12 @@ public class UdfpsController implements DozeReceiver { Log.v(TAG, "onTouch | finger outside"); onFingerUp(); } if ((fromUdfpsView || actionMoveWithinSensorArea) && shouldTryToDismissKeyguard()) { Log.v(TAG, "onTouch | dismiss keyguard from ACTION_MOVE"); mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */); mAttemptedToDismissKeyguard = true; } } Trace.endSection(); break; Loading @@ -448,6 +468,7 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker = null; } Log.v(TAG, "onTouch | finger up"); mAttemptedToDismissKeyguard = false; onFingerUp(); mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION); Trace.endSection(); Loading @@ -459,6 +480,13 @@ public class UdfpsController implements DozeReceiver { return handled; } private boolean shouldTryToDismissKeyguard() { return mView.getAnimationViewController() != null && mView.getAnimationViewController() instanceof UdfpsKeyguardViewController && mKeyguardStateController.canDismissLockScreen() && !mAttemptedToDismissKeyguard; } @Inject public UdfpsController(@NonNull Context context, @NonNull Execution execution, Loading @@ -479,7 +507,8 @@ public class UdfpsController implements DozeReceiver { @NonNull ScreenLifecycle screenLifecycle, @Nullable Vibrator vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @NonNull Optional<UdfpsHbmProvider> hbmProvider) { @NonNull Optional<UdfpsHbmProvider> hbmProvider, @NonNull KeyguardStateController keyguardStateController) { mContext = context; mExecution = execution; // TODO (b/185124905): inject main handler and vibrator once done prototyping Loading @@ -493,6 +522,7 @@ public class UdfpsController implements DozeReceiver { mFgExecutor = fgExecutor; mStatusBar = statusBar; mStatusBarStateController = statusBarStateController; mKeyguardStateController = keyguardStateController; mKeyguardViewManager = statusBarKeyguardViewManager; mDumpManager = dumpManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; Loading Loading @@ -667,6 +697,7 @@ public class UdfpsController implements DozeReceiver { mView.setSensorProperties(mSensorProps); mView.setHbmProvider(mHbmProvider); UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); mAttemptedToDismissKeyguard = false; animation.init(); mView.setAnimationViewController(animation); mOrientationListener.enable(); Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +80 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; Loading Loading @@ -58,6 +59,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; import com.android.systemui.util.concurrency.FakeExecutor; Loading Loading @@ -130,6 +132,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private Vibrator mVibrator; @Mock private UdfpsHapticsSimulator mUdfpsHapticsSimulator; @Mock private KeyguardStateController mKeyguardStateController; private FakeExecutor mFgExecutor; Loading @@ -137,6 +141,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private UdfpsView mUdfpsView; @Mock private UdfpsKeyguardViewController mUdfpsKeyguardViewController; @Mock private TypedArray mBrightnessValues; @Mock private TypedArray mBrightnessBacklight; Loading @@ -149,6 +155,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor; private ScreenLifecycle.Observer mScreenObserver; @Captor private ArgumentCaptor<UdfpsAnimationViewController> mAnimViewControllerCaptor; @Before public void setUp() { setUpResources(); Loading Loading @@ -193,7 +201,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mScreenLifecycle, mVibrator, mUdfpsHapticsSimulator, Optional.of(mHbmProvider)); Optional.of(mHbmProvider), mKeyguardStateController); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); Loading @@ -220,6 +229,76 @@ public class UdfpsControllerTest extends SysuiTestCase { verify(mUdfpsView).dozeTimeTick(); } @Test public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_DOWN is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); downEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); } @Test public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_MOVE is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); } @Test public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN multiple touches are received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); downEvent.recycle(); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); } @Test public void showUdfpsOverlay_addsViewToWindow() throws RemoteException { mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, Loading