From e167286a37dca5cc008ee5fb7660b2b581a11c38 Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Mon, 29 Aug 2022 06:44:32 +0000 Subject: [PATCH 1/4] Use Mockito 4.6.1 FingerprintEnrollFindSensorTest Due to java import has been changed in downstream, update import part to use Mockito 4.6.1 inside FingerprintEnrollFindSensorTest to prevent downstream merge conflict. Bug: 243701933 Test: roboTests FingerprintEnrollFindSensorTest Merged-In: I2cfda684059520f6ddd1e72c55f1ab1ec9c99e8b Change-Id: Idbf7852d95b248293909f73a0cce69de166931c9 --- .../fingerprint/FingerprintEnrollFindSensorTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java index 55f0210dad4..ec17a8602c0 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java @@ -19,9 +19,9 @@ package com.android.settings.biometrics.fingerprint; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.robolectric.RuntimeEnvironment.application; From 561a68db2033a534af8b3611c1a0d6cf5cb72171 Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Thu, 28 Jul 2022 08:15:27 +0000 Subject: [PATCH 2/4] Use setting as base activity for fingerprint Change to use FingerprintSetting as base activity when use launch "Fingerprint Unlock" from Settings -> Security. And then we can prevent that necesssary pop-up activites become full-screen. Bug: 243701933 Bug: 232874879 Test: manual test following cases on fp-only devices, and enable don't keep activity and test them again. 1. fp enrollment on SUW 2. fp add another on SUW 3. add first fp on Security Settings 4. add another fp on Security Settings Test: atest FingerprintStatusUtilsTest BiometricsSafetySourceTest Test: robo test for SetupFingerprintEnrollFindSensorTest SetupFingerprintEnrollFinishTest FingerprintEnrollFindSensorTest FingerprintEnrollEnrollingTest Merged-In: Ib1c2ef9f93fb910eed2930f871c0c69bdb94bcbd Change-Id: Ib1c2ef9f93fb910eed2930f871c0c69bdb94bcbd (cherry picked from commit 84b39c3ed0a2f28e1d3bfc63e985201113d05770) --- .../biometrics/BiometricsEnrollEnrolling.java | 16 +---- .../biometrics/face/FaceEnrollEnrolling.java | 21 ++++++ .../FingerprintEnrollEnrolling.java | 17 ++++- .../FingerprintEnrollFindSensor.java | 13 +++- .../fingerprint/FingerprintEnrollFinish.java | 20 ++---- .../fingerprint/FingerprintSettings.java | 54 ++++++++++++-- .../fingerprint/FingerprintStatusUtils.java | 3 +- .../FingerprintEnrollFindSensorTest.java | 72 +++++++++++++++++++ .../FingerprintStatusUtilsTest.java | 4 +- .../BiometricsSafetySourceTest.java | 3 +- 10 files changed, 178 insertions(+), 45 deletions(-) diff --git a/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java index 3a61d5e2243..2a1d049af1b 100644 --- a/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java @@ -24,8 +24,6 @@ import android.view.View; import com.android.settings.R; import com.android.settings.password.ChooseLockSettingsHelper; -import com.google.android.setupcompat.util.WizardManagerHelper; - /** * Abstract base activity which handles the actual enrolling for biometrics. */ @@ -62,30 +60,18 @@ public abstract class BiometricsEnrollEnrolling extends BiometricEnrollBase @Override protected void onStop() { - super.onStop(); - if (mSidecar != null) { mSidecar.setListener(null); } - if (!isChangingConfigurations()) { if (mSidecar != null) { mSidecar.cancelEnrollment(); getSupportFragmentManager() .beginTransaction().remove(mSidecar).commitAllowingStateLoss(); } - if (!WizardManagerHelper.isAnySetupWizard(getIntent()) - && !BiometricUtils.isAnyMultiBiometricFlow(this)) { - setResult(RESULT_TIMEOUT); - } - finish(); } - } - @Override - protected boolean shouldFinishWhenBackgrounded() { - // Prevent super.onStop() from finishing, since we handle this in our onStop(). - return false; + super.onStop(); } @Override diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java index bf4a1d46187..68fbe3899f1 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java @@ -31,11 +31,13 @@ import com.android.settings.R; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricEnrollSidecar; import com.android.settings.biometrics.BiometricErrorDialog; +import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.BiometricsEnrollEnrolling; import com.android.settings.slices.CustomSliceRegistry; import com.google.android.setupcompat.template.FooterBarMixin; import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupcompat.util.WizardManagerHelper; import java.util.ArrayList; @@ -112,6 +114,25 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { startEnrollment(); } + @Override + protected void onStop() { + if (!isChangingConfigurations()) { + if (!WizardManagerHelper.isAnySetupWizard(getIntent()) + && !BiometricUtils.isAnyMultiBiometricFlow(this)) { + setResult(RESULT_TIMEOUT); + } + finish(); + } + + super.onStop(); + } + + @Override + protected boolean shouldFinishWhenBackgrounded() { + // Prevent super.onStop() from finishing, since we handle this in our onStop(). + return false; + } + @Override public void startEnrollment() { super.startEnrollment(); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index 5f9a74f931a..bad1bbdfc1b 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -339,8 +339,23 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { @Override protected void onStop() { - super.onStop(); + if (!isChangingConfigurations()) { + if (!WizardManagerHelper.isAnySetupWizard(getIntent()) + && !BiometricUtils.isAnyMultiBiometricFlow(this) + && !mFromSettingsSummary) { + setResult(RESULT_TIMEOUT); + } + finish(); + } stopIconAnimation(); + + super.onStop(); + } + + @Override + protected boolean shouldFinishWhenBackgrounded() { + // Prevent super.onStop() from finishing, since we handle this in our onStop(). + return false; } @Override diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java index 627a5143a38..427b50aa7c4 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java @@ -49,8 +49,8 @@ import java.util.List; public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements BiometricEnrollSidecar.Listener { - private static final String TAG = "FingerprintEnrollFindSensor"; + private static final String SAVED_STATE_IS_NEXT_CLICKED = "is_next_clicked"; @Nullable private FingerprintFindSensorAnimation mAnimation; @@ -174,6 +174,15 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements mAnimation = (FingerprintFindSensorAnimation) animationView; } } + if (savedInstanceState != null) { + mNextClicked = savedInstanceState.getBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked); } @Override @@ -240,7 +249,6 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements @Override public void onEnrollmentError(int errMsgId, CharSequence errString) { if (mNextClicked && errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) { - mNextClicked = false; proceedToEnrolling(false /* cancelEnrollment */); } else { FingerprintErrorDialog.showErrorDialog(this, errMsgId); @@ -341,6 +349,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements finish(); } else { // We came back from enrolling but it wasn't completed, start again. + mNextClicked = false; startLookingForFingerprint(); } break; diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java index 16773d3cbda..b606b607a5a 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java @@ -32,7 +32,6 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricUtils; -import com.android.settings.password.ChooseLockSettingsHelper; import com.google.android.setupcompat.template.FooterBarMixin; import com.google.android.setupcompat.template.FooterButton; @@ -114,9 +113,6 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase { setResult(RESULT_FINISHED); if (WizardManagerHelper.isAnySetupWizard(getIntent())) { postEnroll(); - } else if (mFromSettingsSummary) { - // Only launch fingerprint settings if enrollment was triggered through settings summary - launchFingerprintSettings(); } finish(); } @@ -148,21 +144,15 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase { } } - private void launchFingerprintSettings() { - final Intent intent = new Intent(ACTION_FINGERPRINT_SETTINGS); - intent.setPackage(Utils.SETTINGS_PACKAGE_NAME); - intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(Intent.EXTRA_USER_ID, mUserId); - intent.putExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, mChallenge); - startActivity(intent); - overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out); - } - private void onAddAnotherButtonClick(View view) { startActivityForResult(getFingerprintEnrollingIntent(), BiometricUtils.REQUEST_ADD_ANOTHER); } + @Override + protected boolean shouldFinishWhenBackgrounded() { + return !isFinishing() && super.shouldFinishWhenBackgrounded(); + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { updateFingerprintSuggestionEnableState(); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index 46ea7f42351..e1acec72e34 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -22,6 +22,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROF import static android.app.admin.DevicePolicyResources.UNDEFINED; import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; +import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY; import android.app.Activity; import android.app.Dialog; @@ -62,6 +63,7 @@ import com.android.settings.SubSettings; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricUtils; +import com.android.settings.core.SettingsBaseActivity; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; @@ -69,6 +71,7 @@ import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.transition.SettingsTransitionHelper; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.TwoTargetPreference; @@ -129,6 +132,8 @@ public class FingerprintSettings extends SubSettings { private static final String KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE = "fingerprint_enable_keyguard_toggle"; private static final String KEY_LAUNCHED_CONFIRM = "launched_confirm"; + private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled"; + private static final String KEY_IS_ENROLLING = "is_enrolled"; private static final int MSG_REFRESH_FINGERPRINT_TEMPLATES = 1000; private static final int MSG_FINGER_AUTH_SUCCESS = 1001; @@ -140,6 +145,7 @@ public class FingerprintSettings extends SubSettings { private static final int CHOOSE_LOCK_GENERIC_REQUEST = 102; private static final int ADD_FINGERPRINT_REQUEST = 10; + private static final int AUTO_ADD_FIRST_FINGERPRINT_REQUEST = 11; protected static final boolean DEBUG = false; @@ -149,10 +155,11 @@ public class FingerprintSettings extends SubSettings { private boolean mInFingerprintLockout; private byte[] mToken; private boolean mLaunchedConfirm; + private boolean mHasFirstEnrolled = true; private Drawable mHighlightDrawable; private int mUserId; private final List mFooterColumns = new ArrayList<>(); - private boolean mEnrollClicked; + private boolean mIsEnrolling; private long mChallenge; @@ -341,6 +348,9 @@ public class FingerprintSettings extends SubSettings { } mFingerprintsRenaming = new HashMap(); + mUserId = getActivity().getIntent().getIntExtra( + Intent.EXTRA_USER_ID, UserHandle.myUserId()); + mHasFirstEnrolled = mFingerprintManager.hasEnrolledFingerprints(mUserId); if (savedInstanceState != null) { mFingerprintsRenaming = (HashMap) @@ -349,9 +359,10 @@ public class FingerprintSettings extends SubSettings { ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); mLaunchedConfirm = savedInstanceState.getBoolean( KEY_LAUNCHED_CONFIRM, false); + mIsEnrolling = savedInstanceState.getBoolean(KEY_IS_ENROLLING, mIsEnrolling); + mHasFirstEnrolled = savedInstanceState.getBoolean(KEY_HAS_FIRST_ENROLLED, + mHasFirstEnrolled); } - mUserId = getActivity().getIntent().getIntExtra( - Intent.EXTRA_USER_ID, UserHandle.myUserId()); // Need to authenticate a session token if none if (mToken == null && mLaunchedConfirm == false) { @@ -546,7 +557,7 @@ public class FingerprintSettings extends SubSettings { @Override public void onStop() { super.onStop(); - if (!getActivity().isChangingConfigurations() && !mLaunchedConfirm && !mEnrollClicked) { + if (!getActivity().isChangingConfigurations() && !mLaunchedConfirm && !mIsEnrolling) { getActivity().finish(); } } @@ -557,13 +568,15 @@ public class FingerprintSettings extends SubSettings { mToken); outState.putBoolean(KEY_LAUNCHED_CONFIRM, mLaunchedConfirm); outState.putSerializable("mFingerprintsRenaming", mFingerprintsRenaming); + outState.putBoolean(KEY_IS_ENROLLING, mIsEnrolling); + outState.putBoolean(KEY_HAS_FIRST_ENROLLED, mHasFirstEnrolled); } @Override public boolean onPreferenceTreeClick(Preference pref) { final String key = pref.getKey(); if (KEY_FINGERPRINT_ADD.equals(key)) { - mEnrollClicked = true; + mIsEnrolling = true; Intent intent = new Intent(); intent.setClassName(SETTINGS_PACKAGE_NAME, FingerprintEnrollEnrolling.class.getName()); @@ -659,6 +672,11 @@ public class FingerprintSettings extends SubSettings { BiometricUtils.removeGatekeeperPasswordHandle(getActivity(), data); updateAddPreference(); + if (!mHasFirstEnrolled && !mIsEnrolling) { + mIsEnrolling = true; + addFirstFingerprint( + BiometricUtils.getGatekeeperPasswordHandle(data)); + } }); } else { Log.d(TAG, "Data null or GK PW missing"); @@ -669,12 +687,19 @@ public class FingerprintSettings extends SubSettings { finish(); } } else if (requestCode == ADD_FINGERPRINT_REQUEST) { - mEnrollClicked = false; + mIsEnrolling = false; if (resultCode == RESULT_TIMEOUT) { Activity activity = getActivity(); activity.setResult(resultCode); activity.finish(); } + } else if (requestCode == AUTO_ADD_FIRST_FINGERPRINT_REQUEST) { + mIsEnrolling = false; + mHasFirstEnrolled = false; + if (resultCode != RESULT_FINISHED) { + Log.d(TAG, "Add first fingerprint fail, result:" + resultCode); + finish(); + } } } @@ -746,6 +771,23 @@ public class FingerprintSettings extends SubSettings { } } + private void addFirstFingerprint(@Nullable Long gkPwHandle) { + Intent intent = new Intent(); + intent.setClassName(SETTINGS_PACKAGE_NAME, + FingerprintEnrollIntroductionInternal.class.getName()); + + intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true); + intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, + SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE); + + intent.putExtra(Intent.EXTRA_USER_ID, mUserId); + intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken); + if (gkPwHandle != null) { + intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, (long) gkPwHandle); + } + startActivityForResult(intent, AUTO_ADD_FIRST_FINGERPRINT_REQUEST); + } + @VisibleForTesting void deleteFingerPrint(Fingerprint fingerPrint) { mRemovalSidecar.startRemove(fingerPrint, mUserId); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java index 82ceed6c878..71cdcf73b84 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java @@ -78,8 +78,7 @@ public class FingerprintStatusUtils { * Returns the class name of the Settings page corresponding to fingerprint settings. */ public String getSettingsClassName() { - return hasEnrolled() ? FingerprintSettings.class.getName() - : FingerprintEnrollIntroductionInternal.class.getName(); + return FingerprintSettings.class.getName(); } /** diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java index ec17a8602c0..a78dbb1f52a 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java @@ -165,4 +165,76 @@ public class FingerprintEnrollFindSensorTest { null); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(Activity.RESULT_CANCELED); } + + @Test + public void onActivityResult_EnrollRequestResultFinishShallBeSentBack() { + final int defaultActivityResult = Shadows.shadowOf(mActivity).getResultCode(); + + // Start enrolling + EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); + enrollmentCallback.onEnrollmentProgress(123); + enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); + + // onStop shall not change default activity result + mActivity.onStop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(defaultActivityResult); + + // onActivityResult from Enrolling activity shall be sent back + final int testResult = BiometricEnrollBase.RESULT_FINISHED; + mActivity.onActivityResult(BiometricEnrollBase.ENROLL_REQUEST, testResult, null); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + assertThat(mActivity.isFinishing()).isEqualTo(true); + + // onStop shall not change last activity result + mActivity.onStop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + } + + @Test + public void onActivityResult_EnrollRequestResultSkipShallBeSentBack() { + final int defaultActivityResult = Shadows.shadowOf(mActivity).getResultCode(); + + // Start enrolling + EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); + enrollmentCallback.onEnrollmentProgress(123); + enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); + + // onStop shall not change default activity result + mActivity.onStop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(defaultActivityResult); + + // onActivityResult from Enrolling activity shall be sent back + final int testResult = BiometricEnrollBase.RESULT_SKIP; + mActivity.onActivityResult(BiometricEnrollBase.ENROLL_REQUEST, testResult, null); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + assertThat(mActivity.isFinishing()).isEqualTo(true); + + // onStop shall not change last activity result + mActivity.onStop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + } + + @Test + public void onActivityResult_EnrollRequestResultTimeoutShallBeSentBack() { + final int defaultActivityResult = Shadows.shadowOf(mActivity).getResultCode(); + + // Start enrolling + EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); + enrollmentCallback.onEnrollmentProgress(123); + enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); + + // onStop shall not change default activity result + mActivity.onStop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(defaultActivityResult); + + // onActivityResult from Enrolling activity shall be sent back + final int testResult = BiometricEnrollBase.RESULT_TIMEOUT; + mActivity.onActivityResult(BiometricEnrollBase.ENROLL_REQUEST, testResult, null); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + assertThat(mActivity.isFinishing()).isEqualTo(true); + + // onStop shall not change last activity result + mActivity.onStop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + } } diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java index 6faf8011278..69e5e2f485b 100644 --- a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java +++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java @@ -179,11 +179,11 @@ public class FingerprintStatusUtilsTest { } @Test - public void getSettingsClassName_whenNotEnrolled_returnsFingerprintEnrollInduction() { + public void getSettingsClassName_whenNotEnrolled_returnsFingerprintSettings() { when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); assertThat(mFingerprintStatusUtils.getSettingsClassName()) - .isEqualTo(FingerprintEnrollIntroductionInternal.class.getName()); + .isEqualTo(FingerprintSettings.class.getName()); } @Test diff --git a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java index 7ff407db43a..07c1571f987 100644 --- a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java +++ b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java @@ -47,7 +47,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.Settings; import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal; -import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal; import com.android.settings.biometrics.fingerprint.FingerprintSettings; import com.android.settings.testutils.ResourcesUtils; @@ -186,7 +185,7 @@ public class BiometricsSafetySourceTest { assertSafetySourceEnabledDataSetWithSingularSummary( "security_settings_fingerprint_preference_title", "security_settings_fingerprint_preference_summary_none", - FingerprintEnrollIntroductionInternal.class.getName()); + FingerprintSettings.class.getName()); } @Test From d3a1f700ee29e403c19758c47dd592726fe641aa Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Fri, 19 Aug 2022 15:53:58 +0800 Subject: [PATCH 3/4] Fix udfps fingerprint setting flow 1. on udfps + faceunlock devices, fingerprint settings shall not be launched if no fingerprint enrolled. 2. on udfps device, after first fingerprint enrollment successfully, fingerprint settings shall be shown. 3. Update FingerprintEnrollFindSensorTest to support udfps cases. Bug: 243701933 Bug: 243003012 Test: manully adding first fingerprint on udfps + faceunlock device Test: manully suw on udfps + faceunlock device Test: run robotest for FingerprintEnrollFindSensorTest and SetupFingerprintEnrollFindSensorTest Merged-In: I62d945f2c2e980edf2a885234e54acae109e7672 Change-Id: I62d945f2c2e980edf2a885234e54acae109e7672 (cherry picked from commit 936dd31312c210f73af08fea6356c242faadee69) --- .../FingerprintEnrollFindSensor.java | 6 +- .../fingerprint/FingerprintSettings.java | 24 +- .../FingerprintEnrollFindSensorTest.java | 246 ++++++++++++++---- 3 files changed, 205 insertions(+), 71 deletions(-) diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java index 427b50aa7c4..fd589f46dbc 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java @@ -67,7 +67,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class); + final FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(this); final List props = fingerprintManager.getSensorPropertiesInternal(); mCanAssumeUdfps = props != null && props.size() == 1 && props.get(0).isAnyUdfpsType(); @@ -138,8 +138,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements // This is an entry point for SetNewPasswordController, e.g. // adb shell am start -a android.app.action.SET_NEW_PASSWORD if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) { - final FingerprintManager fpm = getSystemService(FingerprintManager.class); - fpm.generateChallenge(mUserId, (sensorId, userId, challenge) -> { + fingerprintManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> { mChallenge = challenge; mSensorId = sensorId; mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge); @@ -278,6 +277,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements } private void onStartButtonClick(View view) { + mNextClicked = true; startActivityForResult(getFingerprintEnrollingIntent(), ENROLL_REQUEST); } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index e1acec72e34..bf18ed513b7 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -364,10 +364,16 @@ public class FingerprintSettings extends SubSettings { mHasFirstEnrolled); } - // Need to authenticate a session token if none - if (mToken == null && mLaunchedConfirm == false) { - mLaunchedConfirm = true; - launchChooseOrConfirmLock(); + // (mLaunchedConfirm or mIsEnrolling) means that we are waiting an activity result. + if (!mLaunchedConfirm && !mIsEnrolling) { + // Need to authenticate a session token if none + if (mToken == null) { + mLaunchedConfirm = true; + launchChooseOrConfirmLock(); + } else if (!mHasFirstEnrolled) { + mIsEnrolling = true; + addFirstFingerprint(); + } } updateFooterColumns(activity); } @@ -674,8 +680,7 @@ public class FingerprintSettings extends SubSettings { updateAddPreference(); if (!mHasFirstEnrolled && !mIsEnrolling) { mIsEnrolling = true; - addFirstFingerprint( - BiometricUtils.getGatekeeperPasswordHandle(data)); + addFirstFingerprint(); } }); } else { @@ -695,7 +700,7 @@ public class FingerprintSettings extends SubSettings { } } else if (requestCode == AUTO_ADD_FIRST_FINGERPRINT_REQUEST) { mIsEnrolling = false; - mHasFirstEnrolled = false; + mHasFirstEnrolled = true; if (resultCode != RESULT_FINISHED) { Log.d(TAG, "Add first fingerprint fail, result:" + resultCode); finish(); @@ -771,7 +776,7 @@ public class FingerprintSettings extends SubSettings { } } - private void addFirstFingerprint(@Nullable Long gkPwHandle) { + private void addFirstFingerprint() { Intent intent = new Intent(); intent.setClassName(SETTINGS_PACKAGE_NAME, FingerprintEnrollIntroductionInternal.class.getName()); @@ -782,9 +787,6 @@ public class FingerprintSettings extends SubSettings { intent.putExtra(Intent.EXTRA_USER_ID, mUserId); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken); - if (gkPwHandle != null) { - intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, (long) gkPwHandle); - } startActivityForResult(intent, AUTO_ADD_FIRST_FINGERPRINT_REQUEST); } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java index a78dbb1f52a..49a16cbc77b 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java @@ -16,11 +16,15 @@ package com.android.settings.biometrics.fingerprint; +import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR; +import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.robolectric.RuntimeEnvironment.application; @@ -28,9 +32,14 @@ import static org.robolectric.RuntimeEnvironment.application; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; +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.CancellationSignal; +import android.view.View; import com.android.settings.R; import com.android.settings.biometrics.BiometricEnrollBase; @@ -40,6 +49,8 @@ import com.android.settings.testutils.shadow.ShadowUtils; import com.google.android.setupcompat.PartnerCustomizationLayout; import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupdesign.GlifLayout; import org.junit.After; import org.junit.Before; @@ -51,17 +62,24 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.Shadows; +import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; import org.robolectric.shadows.ShadowActivity.IntentForResult; +import java.util.ArrayList; + @RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowUtils.class) public class FingerprintEnrollFindSensorTest { + private static final int DEFAULT_ACTIVITY_RESULT = Activity.RESULT_CANCELED; + @Mock private FingerprintManager mFingerprintManager; + private ActivityController mActivityController; + private FingerprintEnrollFindSensor mActivity; @Before @@ -70,12 +88,40 @@ public class FingerprintEnrollFindSensorTest { ShadowUtils.setFingerprintManager(mFingerprintManager); FakeFeatureFactory.setupForTest(); - mActivity = Robolectric.buildActivity( + mActivityController = Robolectric.buildActivity( FingerprintEnrollFindSensor.class, new Intent() // Set the challenge token so the confirm screen will not be shown - .putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0])) - .setup().get(); + .putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]) + ); + mActivity = mActivityController.get(); + } + + private void setupActivity_onRearDevice() { + final ArrayList props = new ArrayList<>(); + props.add(newFingerprintSensorPropertiesInternal(TYPE_REAR)); + doReturn(props).when(mFingerprintManager).getSensorPropertiesInternal(); + + mActivityController.setup(); + } + + private void setupActivity_onUdfpsDevice() { + final ArrayList props = new ArrayList<>(); + props.add(newFingerprintSensorPropertiesInternal(TYPE_UDFPS_OPTICAL)); + doReturn(props).when(mFingerprintManager).getSensorPropertiesInternal(); + + mActivityController.setup(); + } + + private FingerprintSensorPropertiesInternal newFingerprintSensorPropertiesInternal( + @FingerprintSensorProperties.SensorType int sensorType) { + return new FingerprintSensorPropertiesInternal( + 0 /* sensorId */, + SensorProperties.STRENGTH_STRONG, + 1 /* maxEnrollmentsPerUser */, + new ArrayList(), + sensorType, + true /* resetLockoutRequiresHardwareAuthToken */); } @After @@ -83,13 +129,7 @@ public class FingerprintEnrollFindSensorTest { ShadowUtils.reset(); } - @Test - public void enrollFingerprint_shouldProceed() { - EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); - - enrollmentCallback.onEnrollmentProgress(123); - enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); - + private void verifyStartEnrollingActivity() { ShadowActivity shadowActivity = Shadows.shadowOf(mActivity); IntentForResult startedActivity = shadowActivity.getNextStartedActivityForResult(); @@ -100,6 +140,7 @@ public class FingerprintEnrollFindSensorTest { @Test public void enrollFingerprintTwice_shouldStartOneEnrolling() { + setupActivity_onRearDevice(); EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); enrollmentCallback.onEnrollmentProgress(123); @@ -123,6 +164,8 @@ public class FingerprintEnrollFindSensorTest { @Config(qualifiers = "mcc999") @Test public void layoutWithoutAnimation_shouldNotCrash() { + setupActivity_onRearDevice(); + EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); enrollmentCallback.onEnrollmentProgress(123); enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); @@ -137,6 +180,8 @@ public class FingerprintEnrollFindSensorTest { @Test public void clickSkip_shouldReturnResultSkip() { + setupActivity_onRearDevice(); + PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout); layout.getMixin(FooterBarMixin.class).getSecondaryButtonView().performClick(); @@ -160,6 +205,8 @@ public class FingerprintEnrollFindSensorTest { @Test public void onActivityResult_withNullIntentShouldNotCrash() { + setupActivity_onRearDevice(); + // this should not crash mActivity.onActivityResult(BiometricEnrollBase.CONFIRM_REQUEST, Activity.RESULT_OK, null); @@ -167,74 +214,159 @@ public class FingerprintEnrollFindSensorTest { } @Test - public void onActivityResult_EnrollRequestResultFinishShallBeSentBack() { - final int defaultActivityResult = Shadows.shadowOf(mActivity).getResultCode(); - - // Start enrolling - EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); - enrollmentCallback.onEnrollmentProgress(123); - enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); + public void enrollingFinishResultShallSentBack_onRearDevice() { + setupActivity_onRearDevice(); + triggerEnrollProgressAndError_onRearDevice(); + verifyStartEnrollingActivity(); // onStop shall not change default activity result - mActivity.onStop(); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(defaultActivityResult); + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - // onActivityResult from Enrolling activity shall be sent back - final int testResult = BiometricEnrollBase.RESULT_FINISHED; - mActivity.onActivityResult(BiometricEnrollBase.ENROLL_REQUEST, testResult, null); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); - assertThat(mActivity.isFinishing()).isEqualTo(true); - - // onStop shall not change last activity result - mActivity.onStop(); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_FINISHED); } @Test - public void onActivityResult_EnrollRequestResultSkipShallBeSentBack() { - final int defaultActivityResult = Shadows.shadowOf(mActivity).getResultCode(); - - // Start enrolling - EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); - enrollmentCallback.onEnrollmentProgress(123); - enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); + public void enrollingSkipResultShallSentBack_onRearDevice() { + setupActivity_onRearDevice(); + triggerEnrollProgressAndError_onRearDevice(); + verifyStartEnrollingActivity(); // onStop shall not change default activity result - mActivity.onStop(); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(defaultActivityResult); + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - // onActivityResult from Enrolling activity shall be sent back - final int testResult = BiometricEnrollBase.RESULT_SKIP; - mActivity.onActivityResult(BiometricEnrollBase.ENROLL_REQUEST, testResult, null); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); - assertThat(mActivity.isFinishing()).isEqualTo(true); - - // onStop shall not change last activity result - mActivity.onStop(); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_SKIP); } @Test - public void onActivityResult_EnrollRequestResultTimeoutShallBeSentBack() { - final int defaultActivityResult = Shadows.shadowOf(mActivity).getResultCode(); + public void enrollingTimeoutResultShallSentBack_onRearDevice() { + setupActivity_onRearDevice(); + triggerEnrollProgressAndError_onRearDevice(); + verifyStartEnrollingActivity(); - // Start enrolling + // onStop shall not change default activity result + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_TIMEOUT); + } + + @Test + public void enrollingFinishResultShallSentBack_onUdfpsDevice_triggeredByLottieClick() { + setupActivity_onUdfpsDevice(); + clickLottieView_onUdfpsDevice(); + verifyStartEnrollingActivity(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + // onStop shall not change default activity result + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_FINISHED); + } + + @Test + public void enrollingSkipResultShallSentBack_onUdfpsDevice_triggeredByLottieClick() { + setupActivity_onUdfpsDevice(); + clickLottieView_onUdfpsDevice(); + verifyStartEnrollingActivity(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + // onStop shall not change default activity result + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_SKIP); + } + + @Test + public void enrollingTimeoutResultShallSentBack_onUdfpsDevice_triggeredByLottieClick() { + setupActivity_onUdfpsDevice(); + clickLottieView_onUdfpsDevice(); + verifyStartEnrollingActivity(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + // onStop shall not change default activity result + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_TIMEOUT); + } + + @Test + public void enrollingFinishResultShallSentBack_onUdfpsDevice_triggeredByPrimaryButtonClick() { + setupActivity_onUdfpsDevice(); + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // onStop shall not change default activity result + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_FINISHED); + } + + @Test + public void enrollingSkipResultShallSentBack_onUdfpsDevice_triggeredByPrimaryButtonClick() { + setupActivity_onUdfpsDevice(); + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // onStop shall not change default activity result + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_SKIP); + } + + @Test + public void enrollingTimeoutResultShallSentBack_onUdfpsDevice_triggeredByPrimaryButtonClick() { + setupActivity_onUdfpsDevice(); + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // onStop shall not change default activity result + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_TIMEOUT); + } + + private void triggerEnrollProgressAndError_onRearDevice() { EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback(); enrollmentCallback.onEnrollmentProgress(123); enrollmentCallback.onEnrollmentError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "test"); + } - // onStop shall not change default activity result - mActivity.onStop(); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(defaultActivityResult); + private void clickPrimaryButton_onUdfpsDevice() { + final FooterBarMixin footerBarMixin = + ((GlifLayout) mActivity.findViewById(R.id.setup_wizard_layout)) + .getMixin(FooterBarMixin.class); + final FooterButton primaryButton = footerBarMixin.getPrimaryButton(); + assertThat(primaryButton).isNotNull(); + assertThat(primaryButton.getVisibility()).isEqualTo(View.VISIBLE); + primaryButton.onClick(null); + } + private void clickLottieView_onUdfpsDevice() { + final View lottieView = mActivity.findViewById(R.id.illustration_lottie); + assertThat(lottieView).isNotNull(); + lottieView.performClick(); + } + + private void gotEnrollingResult_verifyResultSentBack(int testActivityResult) { // onActivityResult from Enrolling activity shall be sent back - final int testResult = BiometricEnrollBase.RESULT_TIMEOUT; - mActivity.onActivityResult(BiometricEnrollBase.ENROLL_REQUEST, testResult, null); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + mActivityController.start().resume().visible(); + Shadows.shadowOf(mActivity).receiveResult( + new Intent(mActivity, FingerprintEnrollEnrolling.class), + testActivityResult, + null); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testActivityResult); assertThat(mActivity.isFinishing()).isEqualTo(true); // onStop shall not change last activity result - mActivity.onStop(); - assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testResult); + mActivityController.pause().stop(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testActivityResult); } } From af5a7d8dc0c1983751816c694fd91ec9c811ef71 Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Fri, 26 Aug 2022 09:57:40 +0000 Subject: [PATCH 4/4] Bypass cancel error code during "Add another" 1. When user chooses "Add another" on latest fingerprint enroll page, we shall bypass RESULT_CANCELED from FingerprintEnrollEnrolling. This is because user has successfully enrolled a fingerprint before enter FingerprintEnrollFinish page. 2. In FingerprintEnrollFindSensor A. Do not call startLookingForFingerprint() if mNextClick is true because it is wating the result back from FingerprintEnrollEnrolling, and let onActivityResult() mehtod to handle correct behavior. B. Add tests for following cases a. Sidecar existence b. Activity recycled and recreate in order to get activity result. Bug: 243701933 Bug: 243762418 Test: ROBOTEST for SetupFingerprintEnrollFindSensorTest, FingerprintEnrollFindSensorTest, SetupFingerprintEnrollFinishTest Test: Test scenarios w/ and w/o always_finish_activities 1. Enroll a fingerprint but cancel it during enrolling in SuW 2. Enroll a fingerprint in SuW 3. Add another fingerprint in SuW 4. Run "Add another" but cancel it during enrolling in SuW 5. W/o enrolled fingerprint, add first fingerprint in settings 6. W/o enrolled fingerprint, add first fingerprint but cancel it during enrolling in settings 7. W/o enrolled fingerprint, add first fingerprint and choose "Add another" in settings 8. W/o enrolled fingerprint, add first fingerprint and choose "Add another" then cancel it during enrolling in settings 9. W/ 1 enrolled fingerprint, add fingerprint in settings 10. W/ 1 enrolled fingerprint, add fingerprint but cancel it during enrolling in settings 11. W/ 1 enrolled fingerprint, add fingerprint and choose "Add another" in settings 12. W/ 1 enrolled fingerprint, add fingerprint and choose "Add another" then canel it during enrolling in settings Change-Id: I03d8d8ebc39eb34f8fc28acb5cd267e37c7a0311 --- .../FingerprintEnrollFindSensor.java | 21 +- .../fingerprint/FingerprintEnrollFinish.java | 23 +- .../FingerprintEnrollFindSensorTest.java | 334 +++++++++++++++--- 3 files changed, 326 insertions(+), 52 deletions(-) diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java index fd589f46dbc..9abf38d3aa1 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java @@ -134,6 +134,9 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title); setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message); } + if (savedInstanceState != null) { + mNextClicked = savedInstanceState.getBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked); + } // This is an entry point for SetNewPasswordController, e.g. // adb shell am start -a android.app.action.SET_NEW_PASSWORD @@ -148,11 +151,19 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements // it passed in. getIntent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken); - startLookingForFingerprint(); + // Do not start looking for fingerprint if this activity is re-created because it is + // waiting for activity result from enrolling activity. + if (!mNextClicked) { + startLookingForFingerprint(); + } }); } else if (mToken != null) { - // HAT passed in from somewhere else, such as FingerprintEnrollIntroduction - startLookingForFingerprint(); + // Do not start looking for fingerprint if this activity is re-created because it is + // waiting for activity result from enrolling activity. + if (!mNextClicked) { + // HAT passed in from somewhere else, such as FingerprintEnrollIntroduction + startLookingForFingerprint(); + } } else { // There's something wrong with the enrollment flow, this should never happen. throw new IllegalStateException("HAT and GkPwHandle both missing..."); @@ -173,9 +184,6 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements mAnimation = (FingerprintFindSensorAnimation) animationView; } } - if (savedInstanceState != null) { - mNextClicked = savedInstanceState.getBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked); - } } @Override @@ -297,6 +305,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements return; } } + mSidecar.setListener(null); getSupportFragmentManager().beginTransaction().remove(mSidecar). commitAllowingStateLoss(); mSidecar = null; diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java index b606b607a5a..c24e9f0d5e7 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java @@ -50,6 +50,8 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase { static final String FINGERPRINT_SUGGESTION_ACTIVITY = "com.android.settings.SetupFingerprintSuggestionActivity"; + private boolean mIsAddAnotherOrFinish; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -107,9 +109,23 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase { } } + @Override + protected void onStart() { + super.onStart(); + + // Reset it to false every time activity back to fg because this flag is stateless between + // different life cycle. + mIsAddAnotherOrFinish = false; + } + @Override protected void onNextButtonClick(View view) { updateFingerprintSuggestionEnableState(); + finishAndToNext(); + } + + private void finishAndToNext() { + mIsAddAnotherOrFinish = true; setResult(RESULT_FINISHED); if (WizardManagerHelper.isAnySetupWizard(getIntent())) { postEnroll(); @@ -145,20 +161,21 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase { } private void onAddAnotherButtonClick(View view) { + mIsAddAnotherOrFinish = true; startActivityForResult(getFingerprintEnrollingIntent(), BiometricUtils.REQUEST_ADD_ANOTHER); } @Override protected boolean shouldFinishWhenBackgrounded() { - return !isFinishing() && super.shouldFinishWhenBackgrounded(); + return !mIsAddAnotherOrFinish && super.shouldFinishWhenBackgrounded(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { updateFingerprintSuggestionEnableState(); if (requestCode == BiometricUtils.REQUEST_ADD_ANOTHER && resultCode != RESULT_CANCELED) { - setResult(resultCode, data); - finish(); + // If user cancel during "Add another", just use similar flow on "Next" button + finishAndToNext(); } else { super.onActivityResult(requestCode, resultCode, data); } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java index 49a16cbc77b..fad3abf2a3a 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java @@ -19,6 +19,11 @@ package com.android.settings.biometrics.fingerprint; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL; +import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED; +import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_SKIP; +import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_TIMEOUT; +import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.TAG_SIDECAR; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -29,6 +34,7 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.robolectric.RuntimeEnvironment.application; +import android.annotation.NonNull; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; @@ -38,9 +44,12 @@ 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.view.View; +import androidx.fragment.app.Fragment; + import com.android.settings.R; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.password.ChooseLockSettingsHelper; @@ -82,12 +91,7 @@ public class FingerprintEnrollFindSensorTest { private FingerprintEnrollFindSensor mActivity; - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - ShadowUtils.setFingerprintManager(mFingerprintManager); - FakeFeatureFactory.setupForTest(); - + private void buildActivity() { mActivityController = Robolectric.buildActivity( FingerprintEnrollFindSensor.class, new Intent() @@ -97,6 +101,14 @@ public class FingerprintEnrollFindSensorTest { mActivity = mActivityController.get(); } + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + ShadowUtils.setFingerprintManager(mFingerprintManager); + FakeFeatureFactory.setupForTest(); + buildActivity(); + } + private void setupActivity_onRearDevice() { final ArrayList props = new ArrayList<>(); props.add(newFingerprintSensorPropertiesInternal(TYPE_REAR)); @@ -187,7 +199,7 @@ public class FingerprintEnrollFindSensorTest { ShadowActivity shadowActivity = Shadows.shadowOf(mActivity); assertWithMessage("result code").that(shadowActivity.getResultCode()) - .isEqualTo(BiometricEnrollBase.RESULT_SKIP); + .isEqualTo(RESULT_SKIP); } private EnrollmentCallback verifyAndCaptureEnrollmentCallback() { @@ -214,123 +226,320 @@ public class FingerprintEnrollFindSensorTest { } @Test - public void enrollingFinishResultShallSentBack_onRearDevice() { + public void resultFinishShallForward_onRearDevice() { setupActivity_onRearDevice(); triggerEnrollProgressAndError_onRearDevice(); verifyStartEnrollingActivity(); - // onStop shall not change default activity result + // pause activity mActivityController.pause().stop(); + + // onStop shall not change default activity result assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_FINISHED); + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_FINISHED); } @Test - public void enrollingSkipResultShallSentBack_onRearDevice() { + public void resultFinishShallForward_onRearDevice_recreate() { setupActivity_onRearDevice(); triggerEnrollProgressAndError_onRearDevice(); verifyStartEnrollingActivity(); + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + // onStop shall not change default activity result - mActivityController.pause().stop(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_SKIP); + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_FINISHED, bundle); } @Test - public void enrollingTimeoutResultShallSentBack_onRearDevice() { + public void resultSkipShallForward_onRearDevice() { setupActivity_onRearDevice(); + verifySidecar_onRearOrSfpsDevice(); + triggerEnrollProgressAndError_onRearDevice(); verifyStartEnrollingActivity(); - // onStop shall not change default activity result + // pause activity mActivityController.pause().stop(); + + // onStop shall not change default activity result assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_TIMEOUT); + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_SKIP); } @Test - public void enrollingFinishResultShallSentBack_onUdfpsDevice_triggeredByLottieClick() { + public void resultSkipShallForward_onRearDevice_recreate() { + setupActivity_onRearDevice(); + verifySidecar_onRearOrSfpsDevice(); + + triggerEnrollProgressAndError_onRearDevice(); + verifyStartEnrollingActivity(); + + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_SKIP, bundle); + } + + @Test + public void resultTimeoutShallForward_onRearDevice() { + setupActivity_onRearDevice(); + verifySidecar_onRearOrSfpsDevice(); + + triggerEnrollProgressAndError_onRearDevice(); + verifyStartEnrollingActivity(); + + // pause activity + mActivityController.pause().stop(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_TIMEOUT); + } + + @Test + public void resultTimeoutShallForward_onRearDevice_recreate() { + setupActivity_onRearDevice(); + verifySidecar_onRearOrSfpsDevice(); + + triggerEnrollProgressAndError_onRearDevice(); + verifyStartEnrollingActivity(); + + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_TIMEOUT, bundle); + } + + @Test + public void clickLottieResultFinishShallForward_onUdfpsDevice() { setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + clickLottieView_onUdfpsDevice(); verifyStartEnrollingActivity(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - // onStop shall not change default activity result + // pause activity mActivityController.pause().stop(); + + // onStop shall not change default activity result assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_FINISHED); + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_FINISHED); } @Test - public void enrollingSkipResultShallSentBack_onUdfpsDevice_triggeredByLottieClick() { + public void clickLottieResultFinishShallForward_onUdfpsDevice_ifActivityRecycled() { setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + clickLottieView_onUdfpsDevice(); verifyStartEnrollingActivity(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + // onStop shall not change default activity result - mActivityController.pause().stop(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_SKIP); + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_FINISHED, bundle); } @Test - public void enrollingTimeoutResultShallSentBack_onUdfpsDevice_triggeredByLottieClick() { + public void clickLottieResultSkipShallForward_onUdfpsDevice() { setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + clickLottieView_onUdfpsDevice(); verifyStartEnrollingActivity(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - // onStop shall not change default activity result + // pause activity mActivityController.pause().stop(); + + // onStop shall not change default activity result assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_TIMEOUT); + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_SKIP); } @Test - public void enrollingFinishResultShallSentBack_onUdfpsDevice_triggeredByPrimaryButtonClick() { + public void clickLottieResultSkipShallForward_onUdfpsDevice_ifActivityRecycled() { setupActivity_onUdfpsDevice(); - clickPrimaryButton_onUdfpsDevice(); - verifyStartEnrollingActivity(); + verifyNoSidecar(); - // onStop shall not change default activity result - mActivityController.pause().stop(); + clickLottieView_onUdfpsDevice(); + verifyStartEnrollingActivity(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_FINISHED); + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_SKIP, bundle); } @Test - public void enrollingSkipResultShallSentBack_onUdfpsDevice_triggeredByPrimaryButtonClick() { + public void clickLottieResultTimeoutShallForward_onUdfpsDevice() { setupActivity_onUdfpsDevice(); - clickPrimaryButton_onUdfpsDevice(); - verifyStartEnrollingActivity(); + verifyNoSidecar(); - // onStop shall not change default activity result - mActivityController.pause().stop(); + clickLottieView_onUdfpsDevice(); + verifyStartEnrollingActivity(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_SKIP); + // pause activity + mActivityController.pause().stop(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_TIMEOUT); } @Test - public void enrollingTimeoutResultShallSentBack_onUdfpsDevice_triggeredByPrimaryButtonClick() { + public void clickLottieResultTimeoutShallForward_onUdfpsDevice_ifActivityRecycled() { setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + + clickLottieView_onUdfpsDevice(); + verifyStartEnrollingActivity(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_TIMEOUT, bundle); + } + + @Test + public void clickPrimiaryButtonResultFinishShallForward_onUdfpsDevice() { + setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + clickPrimaryButton_onUdfpsDevice(); verifyStartEnrollingActivity(); - // onStop shall not change default activity result + // pause activity mActivityController.pause().stop(); + + // onStop shall not change default activity result assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); - gotEnrollingResult_verifyResultSentBack(BiometricEnrollBase.RESULT_TIMEOUT); + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_FINISHED); + } + + @Test + public void clickPrimiaryButtonResultFinishShallForward_onUdfpsDevice_ifActivityRecycled() { + setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_FINISHED, bundle); + } + + @Test + public void clickPrimiaryButtonResultSkipShallForward_onUdfpsDevice() { + setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // pause activity + mActivityController.pause().stop(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_SKIP); + } + + @Test + public void clickPrimaryButtonResultSkipShallForward_onUdfpsDevice_ifActivityRecycled() { + setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_SKIP, bundle); + } + + @Test + public void clickPrimaryButtonResultTimeoutShallForward_onUdfpsDevice() { + setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // pause activity + mActivityController.pause().stop(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_resumeActivityAndVerifyResultThenForward(RESULT_TIMEOUT); + } + + @Test + public void clickPrimaryButtonResultTimeoutShallForward_onUdfpsDevice_ifActivityRecycled() { + setupActivity_onUdfpsDevice(); + verifyNoSidecar(); + + clickPrimaryButton_onUdfpsDevice(); + verifyStartEnrollingActivity(); + + // recycle activity + final Bundle bundle = new Bundle(); + mActivityController.pause().stop().saveInstanceState(bundle).destroy(); + + // onStop shall not change default activity result + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(DEFAULT_ACTIVITY_RESULT); + + gotEnrollingResult_recreateActivityAndVerifyResultThenForward(RESULT_TIMEOUT, bundle); } private void triggerEnrollProgressAndError_onRearDevice() { @@ -355,9 +564,13 @@ public class FingerprintEnrollFindSensorTest { lottieView.performClick(); } - private void gotEnrollingResult_verifyResultSentBack(int testActivityResult) { - // onActivityResult from Enrolling activity shall be sent back + private void gotEnrollingResult_resumeActivityAndVerifyResultThenForward( + int testActivityResult) { + // resume activity mActivityController.start().resume().visible(); + verifyNoSidecar(); + + // onActivityResult from Enrolling activity shall be forward back Shadows.shadowOf(mActivity).receiveResult( new Intent(mActivity, FingerprintEnrollEnrolling.class), testActivityResult, @@ -366,7 +579,42 @@ public class FingerprintEnrollFindSensorTest { assertThat(mActivity.isFinishing()).isEqualTo(true); // onStop shall not change last activity result - mActivityController.pause().stop(); + mActivityController.pause().stop().destroy(); assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testActivityResult); } + + private void gotEnrollingResult_recreateActivityAndVerifyResultThenForward( + int testActivityResult, @NonNull Bundle savedInstance) { + // Rebuild activity and use savedInstance to restore. + buildActivity(); + mActivityController.setup(savedInstance); + verifyNoSidecar(); + + // onActivityResult from Enrolling activity shall be forward back + Shadows.shadowOf(mActivity).receiveResult( + new Intent(mActivity, FingerprintEnrollEnrolling.class), + testActivityResult, + null); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testActivityResult); + assertThat(mActivity.isFinishing()).isEqualTo(true); + + // onStop shall not change last activity result + mActivityController.pause().stop().destroy(); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(testActivityResult); + } + + private void verifySidecar_onRearOrSfpsDevice() { + final Fragment sidecar = mActivity.getSupportFragmentManager().findFragmentByTag( + TAG_SIDECAR); + assertThat(sidecar).isNotNull(); + assertThat(sidecar.isAdded()).isTrue(); + } + + private void verifyNoSidecar() { + final Fragment sidecar = mActivity.getSupportFragmentManager().findFragmentByTag( + TAG_SIDECAR); + if (sidecar != null) { + assertThat(sidecar.isAdded()).isFalse(); + } + } }