Merge changes from topic "qpr.biometric.2panels" into tm-qpr-dev am: c8f45f75eb

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/19764093

Change-Id: I3b0053910613af42ecc91b8a416b131c662c1837
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index fd589f4..9abf38d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -134,6 +134,9 @@
             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 @@
                 // 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 @@
                 mAnimation = (FingerprintFindSensorAnimation) animationView;
             }
         }
-        if (savedInstanceState != null) {
-            mNextClicked = savedInstanceState.getBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked);
-        }
     }
 
     @Override
@@ -297,6 +305,7 @@
                     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 b606b60..c24e9f0 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
@@ -50,6 +50,8 @@
     static final String FINGERPRINT_SUGGESTION_ACTIVITY =
             "com.android.settings.SetupFingerprintSuggestionActivity";
 
+    private boolean mIsAddAnotherOrFinish;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -108,8 +110,22 @@
     }
 
     @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 @@
     }
 
     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 49a16cb..fad3abf 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 @@
 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.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.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 @@
 
     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 @@
         mActivity = mActivityController.get();
     }
 
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowUtils.setFingerprintManager(mFingerprintManager);
+        FakeFeatureFactory.setupForTest();
+        buildActivity();
+    }
+
     private void setupActivity_onRearDevice() {
         final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
         props.add(newFingerprintSensorPropertiesInternal(TYPE_REAR));
@@ -187,7 +199,7 @@
 
         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 @@
     }
 
     @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 @@
         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 @@
         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();
+        }
+    }
 }