From e944661955ad6f7f453ba4b7ce4c14ce37528cbf Mon Sep 17 00:00:00 2001 From: lbill Date: Mon, 22 Aug 2022 19:59:48 +0000 Subject: [PATCH] Cancel UDFPS enrollment on overlay focus loss Integrate onWindowFocusChanged() to FingerprintEnrollEnrolling When the activity window lost focus, we just cancel this enroll session, and create a dialog to notify user. * Keep legacy rotation behavior, do not cancel enrollment if user rotate device. * This change will cancel enrollment when user interrupted by 1) Swipe down Notification Shade 2) Heads up notification expand 3) Bubble Expanded 4) Google Assist 5) PIP expand 6) Recents 7) Launch Power Menu(Global Action) Test: manual test above scenario Test: m RunSettingsRoboTests -j30 ROBOTEST_FILTER=FingerprintEnrollEnrollingTest Bug: 228261883 Bug: 242478941 Bug: 227905887 Change-Id: Id097e6b15928646cd5ab91c2a1f6eb1bb9bf190b --- .../FingerprintEnrollEnrolling.java | 48 +++++++++++++++---- .../FingerprintEnrollEnrollingTest.java | 42 ++++++++++++++-- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index 5f9a74f931a..67848e3f3d5 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -16,6 +16,8 @@ package com.android.settings.biometrics.fingerprint; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.IntDef; @@ -51,6 +53,7 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.IdRes; import androidx.appcompat.app.AlertDialog; import com.android.internal.annotations.VisibleForTesting; @@ -80,6 +83,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { private static final String TAG = "FingerprintEnrollEnrolling"; static final String TAG_SIDECAR = "sidecar"; static final String KEY_STATE_CANCELED = "is_canceled"; + static final String KEY_STATE_PREVIOUS_ROTATION = "previous_rotation"; private static final int PROGRESS_BAR_MAX = 10000; @@ -134,6 +138,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { private boolean mRestoring; private Vibrator mVibrator; private boolean mIsSetupWizard; + private boolean mIsOrientationChanged; private boolean mIsCanceled; private AccessibilityManager mAccessibilityManager; private boolean mIsAccessibilityEnabled; @@ -155,6 +160,23 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { return defaultDensity == currentDensity; } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + if (hasFocus) { + return; + } + + // By UX design, we should ensure seamless enrollment CUJ even though user rotate device. + // Do NOT cancel enrollment progress after rotating, adding mIsOrientationChanged + // to judge if the focus changed was triggered by rotation, current WMS has triple callbacks + // (true > false > true), we need to reset mIsOrientationChanged when !hasFocus callback. + if (!mIsOrientationChanged) { + onCancelEnrollment(FINGERPRINT_ERROR_USER_CANCELED); + } else { + mIsOrientationChanged = false; + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -295,11 +317,15 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(KEY_STATE_CANCELED, mIsCanceled); + outState.putInt(KEY_STATE_PREVIOUS_ROTATION, mPreviousRotation); } private void restoreSavedState(Bundle savedInstanceState) { mRestoring = true; mIsCanceled = savedInstanceState.getBoolean(KEY_STATE_CANCELED, false); + mPreviousRotation = savedInstanceState.getInt(KEY_STATE_PREVIOUS_ROTATION, + getDisplay().getRotation()); + mIsOrientationChanged = mPreviousRotation != getDisplay().getRotation(); } @Override @@ -337,6 +363,19 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { } } + @VisibleForTesting + void onCancelEnrollment(@IdRes int errorMsgId) { + FingerprintErrorDialog.showErrorDialog(this, errorMsgId); + mIsCanceled = true; + mIsOrientationChanged = false; + cancelEnrollment(); + stopIconAnimation(); + stopListenOrientationEvent(); + if (!mCanAssumeUdfps) { + mErrorText.removeCallbacks(mTouchAgainRunnable); + } + } + @Override protected void onStop() { super.onStop(); @@ -541,14 +580,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { @Override public void onEnrollmentError(int errMsgId, CharSequence errString) { - FingerprintErrorDialog.showErrorDialog(this, errMsgId); - mIsCanceled = true; - cancelEnrollment(); - stopIconAnimation(); - stopListenOrientationEvent(); - if (!mCanAssumeUdfps) { - mErrorText.removeCallbacks(mTouchAgainRunnable); - } + onCancelEnrollment(errMsgId); } @Override diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java index 757a3043f40..73956947b50 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java @@ -16,6 +16,8 @@ package com.android.settings.biometrics.fingerprint; +import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.KEY_STATE_PREVIOUS_ROTATION; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -28,14 +30,18 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Context; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.os.Bundle; import android.os.CancellationSignal; import android.os.Vibrator; +import android.view.Display; +import android.view.Surface; import android.widget.TextView; import com.android.settings.R; @@ -49,6 +55,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; import org.robolectric.android.controller.ActivityController; import java.util.ArrayList; @@ -61,7 +68,10 @@ public class FingerprintEnrollEnrollingTest { @Mock private Vibrator mVibrator; + @Mock private Display mMockDisplay; + private FingerprintEnrollEnrolling mActivity; + private Context mContext; @Before public void setUp() { @@ -100,6 +110,26 @@ public class FingerprintEnrollEnrollingTest { verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any()); } + @Test + public void fingerprintUdfpsOverlayEnrollment_gainFocus_shouldNotCancel() { + initializeActivityFor(FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); + + mActivity.onEnrollmentProgressChange(1, 1); + mActivity.onWindowFocusChanged(true); + + verify(mActivity, never()).onCancelEnrollment(anyInt()); + } + + @Test + public void fingerprintUdfpsOverlayEnrollment_loseFocus_shouldCancel() { + initializeActivityFor(FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); + + mActivity.onEnrollmentProgressChange(1, 1); + mActivity.onWindowFocusChanged(false); + + verify(mActivity, never()).onCancelEnrollment(anyInt()); + } + private void initializeActivityFor(int sensorType) { final List componentInfo = new ArrayList<>(); final FingerprintSensorPropertiesInternal prop = @@ -111,15 +141,21 @@ public class FingerprintEnrollEnrollingTest { sensorType, true /* resetLockoutRequiresHardwareAuthToken */); final ArrayList props = new ArrayList<>(); + final Bundle savedInstanceState = new Bundle(); + savedInstanceState.putInt(KEY_STATE_PREVIOUS_ROTATION, Surface.ROTATION_90); props.add(prop); - when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); - + mContext = spy(RuntimeEnvironment.application); mActivity = spy(FingerprintEnrollEnrolling.class); + + when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); + when(mContext.getDisplay()).thenReturn(mMockDisplay); + when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0); + doReturn(true).when(mActivity).shouldShowLottie(); doReturn(mFingerprintManager).when(mActivity).getSystemService(FingerprintManager.class); doReturn(mVibrator).when(mActivity).getSystemService(Vibrator.class); - ActivityController.of(mActivity).create(); + ActivityController.of(mActivity).create(savedInstanceState); } private EnrollmentCallback verifyAndCaptureEnrollmentCallback() {