diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index ab3e042e9da71..c292dc4b988f8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -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; @@ -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; @@ -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 = @@ -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. @@ -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; @@ -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). @@ -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; @@ -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(); @@ -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, @@ -479,7 +507,8 @@ public class UdfpsController implements DozeReceiver { @NonNull ScreenLifecycle screenLifecycle, @Nullable Vibrator vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, - @NonNull Optional hbmProvider) { + @NonNull Optional hbmProvider, + @NonNull KeyguardStateController keyguardStateController) { mContext = context; mExecution = execution; // TODO (b/185124905): inject main handler and vibrator once done prototyping @@ -493,6 +522,7 @@ public class UdfpsController implements DozeReceiver { mFgExecutor = fgExecutor; mStatusBar = statusBar; mStatusBarStateController = statusBarStateController; + mKeyguardStateController = keyguardStateController; mKeyguardViewManager = statusBarKeyguardViewManager; mDumpManager = dumpManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -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(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index d8d3676d4fa24..5156d7c2e87ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -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; @@ -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; @@ -130,6 +132,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private Vibrator mVibrator; @Mock private UdfpsHapticsSimulator mUdfpsHapticsSimulator; + @Mock + private KeyguardStateController mKeyguardStateController; private FakeExecutor mFgExecutor; @@ -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; @@ -149,6 +155,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor mScreenObserverCaptor; private ScreenLifecycle.Observer mScreenObserver; + @Captor private ArgumentCaptor mAnimViewControllerCaptor; + @Before public void setUp() { setUpResources(); @@ -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()); @@ -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,