[BiometricsV2] Refactor AutoCredentialViewModel

Refactor AutoCredentialViewModelTest and FingerprintEnrollmentViewModel
to kotlin and change LiveData to Flow

Bug: 286197659
Test: atest -m CredentialModelTest
Test: atest -m AutoCredentialViewModelTest
Test: atest -m FingerprintEnrollmentViewModelTest
Test: atest -m FingerprintEnrollmentActivityTest
Test: atest -m biometrics-enrollment-test
Change-Id: I84bab0b46e023303c0046a6ae6886ab1cf9458b8
diff --git a/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java b/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java
index b83614c..2cf607a 100644
--- a/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java
+++ b/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java
@@ -28,6 +28,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.biometrics.fingerprint.FingerprintUpdater;
 import com.android.settings.biometrics2.data.repository.FingerprintRepository;
+import com.android.settings.biometrics2.ui.model.CredentialModel;
 import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
 import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
 import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator;
@@ -54,8 +55,8 @@
             new CreationExtras.Key<ChallengeGenerator>() {};
     public static final CreationExtras.Key<EnrollmentRequest> ENROLLMENT_REQUEST_KEY =
             new CreationExtras.Key<EnrollmentRequest>() {};
-    public static final CreationExtras.Key<Integer> USER_ID_KEY =
-            new CreationExtras.Key<Integer>() {};
+    public static final CreationExtras.Key<CredentialModel> CREDENTIAL_MODEL_KEY =
+            new CreationExtras.Key<CredentialModel>() {};
 
     @NonNull
     @Override
@@ -76,9 +77,10 @@
             final LockPatternUtils lockPatternUtils =
                     featureFactory.getSecurityFeatureProvider().getLockPatternUtils(application);
             final ChallengeGenerator challengeGenerator = extras.get(CHALLENGE_GENERATOR_KEY);
-            if (challengeGenerator != null) {
+            final CredentialModel credentialModel = extras.get(CREDENTIAL_MODEL_KEY);
+            if (challengeGenerator != null && credentialModel != null) {
                 return (T) new AutoCredentialViewModel(application, lockPatternUtils,
-                        challengeGenerator);
+                        challengeGenerator, credentialModel);
             }
         } else if (modelClass.isAssignableFrom(DeviceFoldedViewModel.class)) {
             return (T) new DeviceFoldedViewModel(new ScreenSizeFoldProvider(application),
@@ -93,10 +95,10 @@
         } else if (modelClass.isAssignableFrom(FingerprintEnrollIntroViewModel.class)) {
             final FingerprintRepository repository = provider.getFingerprintRepository(application);
             final EnrollmentRequest request = extras.get(ENROLLMENT_REQUEST_KEY);
-            final Integer userId = extras.get(USER_ID_KEY);
-            if (repository != null && request != null && userId != null) {
+            final CredentialModel credentialModel = extras.get(CREDENTIAL_MODEL_KEY);
+            if (repository != null && request != null && credentialModel != null) {
                 return (T) new FingerprintEnrollIntroViewModel(application, repository, request,
-                        userId);
+                        credentialModel.getUserId());
             }
         } else if (modelClass.isAssignableFrom(FingerprintEnrollmentViewModel.class)) {
             final FingerprintRepository repository = provider.getFingerprintRepository(application);
@@ -105,27 +107,27 @@
                 return (T) new FingerprintEnrollmentViewModel(application, repository, request);
             }
         } else if (modelClass.isAssignableFrom(FingerprintEnrollProgressViewModel.class)) {
-            final Integer userId = extras.get(USER_ID_KEY);
-            if (userId != null) {
+            final CredentialModel credentialModel = extras.get(CREDENTIAL_MODEL_KEY);
+            if (credentialModel != null) {
                 return (T) new FingerprintEnrollProgressViewModel(application,
-                        new FingerprintUpdater(application), userId);
+                        new FingerprintUpdater(application), credentialModel.getUserId());
             }
         } else if (modelClass.isAssignableFrom(FingerprintEnrollEnrollingViewModel.class)) {
-            final Integer userId = extras.get(USER_ID_KEY);
+            final CredentialModel credentialModel = extras.get(CREDENTIAL_MODEL_KEY);
             final FingerprintRepository fingerprint = provider.getFingerprintRepository(
                     application);
-            if (fingerprint != null && userId != null) {
-                return (T) new FingerprintEnrollEnrollingViewModel(application, userId,
-                        fingerprint);
+            if (fingerprint != null && credentialModel != null) {
+                return (T) new FingerprintEnrollEnrollingViewModel(application,
+                        credentialModel.getUserId(), fingerprint);
             }
         } else if (modelClass.isAssignableFrom(FingerprintEnrollFinishViewModel.class)) {
-            final Integer userId = extras.get(USER_ID_KEY);
+            final CredentialModel credentialModel = extras.get(CREDENTIAL_MODEL_KEY);
             final EnrollmentRequest request = extras.get(ENROLLMENT_REQUEST_KEY);
             final FingerprintRepository fingerprint = provider.getFingerprintRepository(
                     application);
-            if (fingerprint != null && userId != null && request != null) {
-                return (T) new FingerprintEnrollFinishViewModel(application, userId, request,
-                        fingerprint);
+            if (fingerprint != null && credentialModel != null && request != null) {
+                return (T) new FingerprintEnrollFinishViewModel(application,
+                        credentialModel.getUserId(), request, fingerprint);
             }
         } else if (modelClass.isAssignableFrom(FingerprintEnrollErrorDialogViewModel.class)) {
             final EnrollmentRequest request = extras.get(ENROLLMENT_REQUEST_KEY);
diff --git a/src/com/android/settings/biometrics2/ui/model/CredentialModel.kt b/src/com/android/settings/biometrics2/ui/model/CredentialModel.kt
index 7999ab8..5350733 100644
--- a/src/com/android/settings/biometrics2/ui/model/CredentialModel.kt
+++ b/src/com/android/settings/biometrics2/ui/model/CredentialModel.kt
@@ -80,20 +80,6 @@
     val isValidToken: Boolean
         get() = token != null
 
-    val bundle: Bundle
-        /**
-         * Get a bundle which can be used to recreate CredentialModel
-         */
-        get() {
-            val bundle = Bundle()
-            bundle.putInt(EXTRA_USER_ID, userId)
-            bundle.putLong(EXTRA_KEY_CHALLENGE, challenge)
-            bundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, token)
-            bundle.putLong(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
-            return bundle
-        }
-
-
     /** Returns a string representation of the object */
     override fun toString(): String {
         val gkPwHandleLen = "$gkPwHandle".length
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
index fa8a3fa..562b7dd 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
@@ -44,16 +44,13 @@
 import com.android.settings.biometrics.BiometricEnrollBase
 import com.android.settings.biometrics2.factory.BiometricsViewModelFactory
 import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY
+import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CREDENTIAL_MODEL_KEY
 import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.ENROLLMENT_REQUEST_KEY
-import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.USER_ID_KEY
 import com.android.settings.biometrics2.ui.model.CredentialModel
 import com.android.settings.biometrics2.ui.model.EnrollmentRequest
 import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel
-import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK
-import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK
-import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE
-import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID
 import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator
+import com.android.settings.biometrics2.ui.viewmodel.CredentialAction
 import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel
 import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
 import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE
@@ -170,7 +167,6 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        autoCredentialViewModel.setCredentialModel(savedInstanceState, intent)
 
         // Theme
         setTheme(viewModel.request.theme)
@@ -219,15 +215,24 @@
             }
         }
 
-        // observe LiveData
-        viewModel.setResultLiveData.observe(this) {
-            result: ActivityResult -> onSetActivityResult(result)
-        }
-        autoCredentialViewModel.generateChallengeFailedLiveData.observe(this) {
-            _: Boolean -> onGenerateChallengeFailed()
-        }
+        collectFlows()
+    }
+
+    private fun collectFlows() {
         lifecycleScope.launch {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
+                viewModel.setResultFlow.collect {
+                    Log.d(TAG, "setResultLiveData($it)")
+                    onSetActivityResult(it)
+                }
+            }
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                autoCredentialViewModel.generateChallengeFailedFlow.collect {
+                    Log.d(TAG, "generateChallengeFailedFlow($it)")
+                    onSetActivityResult(ActivityResult(RESULT_CANCELED, null))
+                }
+            }
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
                 errorDialogViewModel.newDialogFlow.collect {
                     Log.d(TAG, "newErrorDialogFlow($it)")
                     FingerprintEnrollErrorDialog.newInstance(it).show(
@@ -236,8 +241,6 @@
                     )
                 }
             }
-        }
-        lifecycleScope.launch {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 errorDialogViewModel.setResultFlow.collect {
                     Log.d(TAG, "errorDialogSetResultFlow($it)")
@@ -408,10 +411,6 @@
         }
     }
 
-    private fun onGenerateChallengeFailed() {
-        onSetActivityResult(ActivityResult(RESULT_CANCELED, null))
-    }
-
     private fun onSetActivityResult(result: ActivityResult) {
         val challengeExtras: Bundle? = autoCredentialViewModel.createGeneratingChallengeExtras()
         val overrideResult: ActivityResult = viewModel.getOverrideActivityResult(
@@ -428,8 +427,8 @@
     }
 
     private fun checkCredential() {
-        when (autoCredentialViewModel.checkCredential()) {
-            CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK -> {
+        when (autoCredentialViewModel.checkCredential(lifecycleScope)) {
+            CredentialAction.FAIL_NEED_TO_CHOOSE_LOCK -> {
                 val intent: Intent = autoCredentialViewModel.createChooseLockIntent(
                     this,
                     viewModel.request.isSuw,
@@ -442,7 +441,7 @@
                 return
             }
 
-            CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK -> {
+            CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK -> {
                 val launched: Boolean = autoCredentialViewModel.createConfirmLockLauncher(
                     this,
                     LAUNCH_CONFIRM_LOCK_ACTIVITY,
@@ -459,21 +458,24 @@
                 return
             }
 
-            CREDENTIAL_VALID,
-            CREDENTIAL_IS_GENERATING_CHALLENGE -> {}
+            CredentialAction.CREDENTIAL_VALID,
+            CredentialAction.IS_GENERATING_CHALLENGE -> {}
         }
     }
 
-    private fun onChooseOrConfirmLockResult(isChooseLock: Boolean, activityResult: ActivityResult) {
+    private fun onChooseOrConfirmLockResult(
+        isChooseLock: Boolean,
+        activityResult: ActivityResult
+    ) {
         if (!viewModel.isWaitingActivityResult.compareAndSet(true, false)) {
             Log.w(TAG, "isChooseLock:$isChooseLock, fail to unset waiting flag")
         }
-        if (autoCredentialViewModel.checkNewCredentialFromActivityResult(
-                isChooseLock, activityResult
+        if (!autoCredentialViewModel.generateChallengeAsCredentialActivityResult(
+                isChooseLock,
+                activityResult,
+                lifecycleScope
             )
         ) {
-            overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out)
-        } else {
             onSetActivityResult(activityResult)
         }
     }
@@ -573,7 +575,11 @@
 
     override fun onPause() {
         super.onPause()
-        viewModel.checkFinishActivityDuringOnPause(isFinishing, isChangingConfigurations)
+        viewModel.checkFinishActivityDuringOnPause(
+            isFinishing,
+            isChangingConfigurations,
+            lifecycleScope
+        )
     }
 
     override fun onDestroy() {
@@ -596,17 +602,14 @@
     }
 
     override val defaultViewModelCreationExtras: CreationExtras
-        get() {
-            val fingerprintRepository = featureFactory.biometricsRepositoryProvider
-                .getFingerprintRepository(application)!!
-            val credentialModel = CredentialModel(intent.extras, SystemClock.elapsedRealtimeClock())
-
-            return MutableCreationExtras(super.defaultViewModelCreationExtras).also {
-                it[CHALLENGE_GENERATOR_KEY] = FingerprintChallengeGenerator(fingerprintRepository)
-                it[ENROLLMENT_REQUEST_KEY] =
-                    EnrollmentRequest(intent, applicationContext, this is SetupActivity)
-                it[USER_ID_KEY] = credentialModel.userId
-            }
+        get() = MutableCreationExtras(super.defaultViewModelCreationExtras).also {
+            it[CHALLENGE_GENERATOR_KEY] = FingerprintChallengeGenerator(
+                featureFactory.biometricsRepositoryProvider.getFingerprintRepository(application)!!
+            )
+            it[ENROLLMENT_REQUEST_KEY] =
+                EnrollmentRequest(intent, applicationContext, this is SetupActivity)
+            it[CREDENTIAL_MODEL_KEY] =
+                CredentialModel(intent.extras, SystemClock.elapsedRealtimeClock())
         }
 
     override val defaultViewModelProviderFactory: ViewModelProvider.Factory
@@ -630,11 +633,6 @@
         super.onConfigurationChanged(newConfig)
     }
 
-    override fun onSaveInstanceState(outState: Bundle) {
-        super.onSaveInstanceState(outState)
-        autoCredentialViewModel.onSaveInstanceState(outState)
-    }
-
     companion object {
         private const val DEBUG = false
         private const val TAG = "FingerprintEnrollmentActivity"
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java
deleted file mode 100644
index 7e48f82..0000000
--- a/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.biometrics2.ui.viewmodel;
-
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
-import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
-import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_GK_PW_HANDLE;
-import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
-
-import android.annotation.IntDef;
-import android.app.Activity;
-import android.app.Application;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
-
-import androidx.activity.result.ActivityResult;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.settings.biometrics.BiometricUtils;
-import com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
-import com.android.settings.biometrics2.data.repository.FingerprintRepository;
-import com.android.settings.biometrics2.ui.model.CredentialModel;
-import com.android.settings.password.ChooseLockGeneric;
-import com.android.settings.password.ChooseLockPattern;
-import com.android.settings.password.ChooseLockSettingsHelper;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * AutoCredentialViewModel which uses CredentialModel to determine next actions for activity, like
- * start ChooseLockActivity, start ConfirmLockActivity, GenerateCredential, or do nothing.
- */
-public class AutoCredentialViewModel extends AndroidViewModel {
-
-    private static final String TAG = "AutoCredentialViewModel";
-
-    @VisibleForTesting
-    static final String KEY_CREDENTIAL_MODEL = "credential_model";
-
-    @VisibleForTesting
-    static final String KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL =
-            "is_generating_challenge_during_checking_credential";
-
-    private static final boolean DEBUG = false;
-
-    /**
-     * Valid credential, activity does nothing.
-     */
-    public static final int CREDENTIAL_VALID = 0;
-
-    /**
-     * This credential looks good, but still need to run generateChallenge().
-     */
-    public static final int CREDENTIAL_IS_GENERATING_CHALLENGE = 1;
-
-    /**
-     * Need activity to run choose lock
-     */
-    public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 2;
-
-    /**
-     * Need activity to run confirm lock
-     */
-    public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 3;
-
-    @IntDef(prefix = { "CREDENTIAL_" }, value = {
-            CREDENTIAL_VALID,
-            CREDENTIAL_IS_GENERATING_CHALLENGE,
-            CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK,
-            CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CredentialAction {}
-
-    /**
-     * Generic callback for FingerprintManager#generateChallenge or FaceManager#generateChallenge
-     */
-    public interface GenerateChallengeCallback {
-        /**
-         * Generic generateChallenge method for FingerprintManager or FaceManager
-         */
-        void onChallengeGenerated(int sensorId, int userId, long challenge);
-    }
-
-    /**
-     * A generic interface class for calling different generateChallenge from FingerprintManager or
-     * FaceManager
-     */
-    public interface ChallengeGenerator {
-        /**
-         * Get callback that will be called later after challenge generated
-         */
-        @Nullable
-        GenerateChallengeCallback getCallback();
-
-        /**
-         * Set callback that will be called later after challenge generated
-         */
-        void setCallback(@Nullable GenerateChallengeCallback callback);
-
-        /**
-         * Method for generating challenge from FingerprintManager or FaceManager
-         */
-        void generateChallenge(int userId);
-    }
-
-    /**
-     * Used to generate challenge through FingerprintRepository
-     */
-    public static class FingerprintChallengeGenerator implements ChallengeGenerator {
-
-        private static final String TAG = "FingerprintChallengeGenerator";
-
-        @NonNull
-        private final FingerprintRepository mFingerprintRepository;
-
-        @Nullable
-        private GenerateChallengeCallback mCallback = null;
-
-        public FingerprintChallengeGenerator(@NonNull FingerprintRepository fingerprintRepository) {
-            mFingerprintRepository = fingerprintRepository;
-        }
-
-        @Nullable
-        @Override
-        public GenerateChallengeCallback getCallback() {
-            return mCallback;
-        }
-
-        @Override
-        public void setCallback(@Nullable GenerateChallengeCallback callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void generateChallenge(int userId) {
-            final GenerateChallengeCallback callback = mCallback;
-            if (callback == null) {
-                Log.e(TAG, "generateChallenge, null callback");
-                return;
-            }
-            mFingerprintRepository.generateChallenge(userId, callback::onChallengeGenerated);
-        }
-    }
-
-    @NonNull private final LockPatternUtils mLockPatternUtils;
-    @NonNull private final ChallengeGenerator mChallengeGenerator;
-    private CredentialModel mCredentialModel = null;
-    @NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailedLiveData =
-            new MutableLiveData<>();
-
-    // flag if token is generating through checkCredential()'s generateChallenge()
-    private boolean mIsGeneratingChallengeDuringCheckingCredential;
-
-    public AutoCredentialViewModel(
-            @NonNull Application application,
-            @NonNull LockPatternUtils lockPatternUtils,
-            @NonNull ChallengeGenerator challengeGenerator) {
-        super(application);
-        mLockPatternUtils = lockPatternUtils;
-        mChallengeGenerator = challengeGenerator;
-    }
-
-    /**
-     * Set CredentialModel, the source is coming from savedInstanceState or activity intent
-     */
-    public void setCredentialModel(@Nullable Bundle savedInstanceState, @NonNull Intent intent) {
-        final Bundle bundle;
-        if (savedInstanceState != null) {
-            bundle = savedInstanceState.getBundle(KEY_CREDENTIAL_MODEL);
-            mIsGeneratingChallengeDuringCheckingCredential = savedInstanceState.getBoolean(
-                    KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL);
-        } else {
-            bundle = intent.getExtras();
-        }
-        mCredentialModel = new CredentialModel(bundle, SystemClock.elapsedRealtimeClock());
-
-        if (DEBUG) {
-            Log.d(TAG, "setCredentialModel " + mCredentialModel + ", savedInstanceState exist:"
-                    + (savedInstanceState != null));
-        }
-    }
-
-    /**
-     * Handle onSaveInstanceState from activity
-     */
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        outState.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL,
-                mIsGeneratingChallengeDuringCheckingCredential);
-        outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle());
-    }
-
-    @NonNull
-    public LiveData<Boolean> getGenerateChallengeFailedLiveData() {
-        return mGenerateChallengeFailedLiveData;
-    }
-
-    /**
-     * Get bundle which passing back to FingerprintSettings for late generateChallenge()
-     */
-    @Nullable
-    public Bundle createGeneratingChallengeExtras() {
-        if (!mIsGeneratingChallengeDuringCheckingCredential
-                || !mCredentialModel.isValidToken()
-                || !mCredentialModel.isValidChallenge()) {
-            return null;
-        }
-
-        Bundle bundle = new Bundle();
-        bundle.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
-                mCredentialModel.getToken());
-        bundle.putLong(EXTRA_KEY_CHALLENGE, mCredentialModel.getChallenge());
-        return bundle;
-    }
-
-    /**
-     * Check credential status for biometric enrollment.
-     */
-    @CredentialAction
-    public int checkCredential() {
-        if (isValidCredential()) {
-            return CREDENTIAL_VALID;
-        }
-        if (isUnspecifiedPassword()) {
-            return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
-        } else if (mCredentialModel.isValidGkPwHandle()) {
-            final long gkPwHandle = mCredentialModel.getGkPwHandle();
-            mCredentialModel.clearGkPwHandle();
-            // GkPwHandle is got through caller activity, we shall not revoke it after
-            // generateChallenge(). Let caller activity to make decision.
-            generateChallenge(gkPwHandle, false /* revokeGkPwHandle */);
-            mIsGeneratingChallengeDuringCheckingCredential = true;
-            return CREDENTIAL_IS_GENERATING_CHALLENGE;
-        } else {
-            return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
-        }
-    }
-
-    private void generateChallenge(long gkPwHandle, boolean revokeGkPwHandle) {
-        mChallengeGenerator.setCallback((sensorId, userId, challenge) -> {
-            try {
-                final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId);
-                mCredentialModel.setChallenge(challenge);
-                mCredentialModel.setToken(newToken);
-            } catch (IllegalStateException e) {
-                Log.e(TAG, "generateChallenge, IllegalStateException", e);
-                mGenerateChallengeFailedLiveData.postValue(true);
-                return;
-            }
-
-            if (revokeGkPwHandle) {
-                mLockPatternUtils.removeGatekeeperPasswordHandle(gkPwHandle);
-            }
-
-            if (DEBUG) {
-                Log.d(TAG, "generateChallenge(), model:" + mCredentialModel
-                        + ", revokeGkPwHandle:" + revokeGkPwHandle);
-            }
-
-            // Check credential again
-            if (!isValidCredential()) {
-                Log.w(TAG, "generateChallenge, invalid Credential");
-                mGenerateChallengeFailedLiveData.postValue(true);
-            }
-        });
-        mChallengeGenerator.generateChallenge(getUserId());
-    }
-
-    private boolean isValidCredential() {
-        return !isUnspecifiedPassword() && mCredentialModel.isValidToken();
-    }
-
-    private boolean isUnspecifiedPassword() {
-        return mLockPatternUtils.getActivePasswordQuality(getUserId())
-                == PASSWORD_QUALITY_UNSPECIFIED;
-    }
-
-    /**
-     * Handle activity result from ChooseLockGeneric, ConfirmLockPassword, or ConfirmLockPattern
-     * @param isChooseLock true if result is coming from ChooseLockGeneric. False if result is
-     *                     coming from ConfirmLockPassword or ConfirmLockPattern
-     * @param result activity result
-     * @return if it is a valid result
-     */
-    public boolean checkNewCredentialFromActivityResult(boolean isChooseLock,
-            @NonNull ActivityResult result) {
-        if ((isChooseLock && result.getResultCode() == ChooseLockPattern.RESULT_FINISHED)
-                || (!isChooseLock && result.getResultCode() == Activity.RESULT_OK)) {
-            final Intent data = result.getData();
-            if (data != null) {
-                final long gkPwHandle = result.getData().getLongExtra(
-                        EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE);
-                // Revoke self requested GkPwHandle because it shall only used once inside this
-                // activity lifecycle.
-                generateChallenge(gkPwHandle, true /* revokeGkPwHandle */);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Get userId for this credential
-     */
-    public int getUserId() {
-        return mCredentialModel.getUserId();
-    }
-
-    /**
-     * Get userId for this credential
-     */
-    @Nullable
-    public byte[] getToken() {
-        return mCredentialModel.getToken();
-    }
-
-    @Nullable
-    private byte[] requestGatekeeperHat(long gkPwHandle, long challenge, int userId)
-            throws IllegalStateException {
-        final VerifyCredentialResponse response = mLockPatternUtils
-                .verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId);
-        if (!response.isMatched()) {
-            throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT");
-        }
-        return response.getGatekeeperHAT();
-    }
-
-    /**
-     * Create Intent for choosing lock
-     */
-    @NonNull
-    public Intent createChooseLockIntent(@NonNull Context context, boolean isSuw,
-            @NonNull Bundle suwExtras) {
-        final Intent intent = BiometricUtils.getChooseLockIntent(context, isSuw,
-                suwExtras);
-        intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS,
-                true);
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
-        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
-
-        if (mCredentialModel.isValidUserId()) {
-            intent.putExtra(Intent.EXTRA_USER_ID, mCredentialModel.getUserId());
-        }
-        return intent;
-    }
-
-    /**
-     * Create ConfirmLockLauncher
-     */
-    @NonNull
-    public ChooseLockSettingsHelper createConfirmLockLauncher(@NonNull Activity activity,
-            int requestCode, @NonNull String title) {
-        final ChooseLockSettingsHelper.Builder builder =
-                new ChooseLockSettingsHelper.Builder(activity);
-        builder.setRequestCode(requestCode)
-                .setTitle(title)
-                .setRequestGatekeeperPasswordHandle(true)
-                .setForegroundOnly(true)
-                .setReturnCredentials(true);
-
-        if (mCredentialModel.isValidUserId()) {
-            builder.setUserId(mCredentialModel.getUserId());
-        }
-        return builder.build();
-    }
-
-}
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.kt b/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.kt
new file mode 100644
index 0000000..3fd4d51
--- /dev/null
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.biometrics2.ui.viewmodel
+
+import android.app.Activity
+import android.app.Application
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.result.ActivityResult
+import androidx.lifecycle.AndroidViewModel
+import com.android.internal.widget.LockPatternUtils
+import com.android.settings.biometrics.BiometricEnrollBase
+import com.android.settings.biometrics.BiometricUtils
+import com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException
+import com.android.settings.biometrics2.data.repository.FingerprintRepository
+import com.android.settings.biometrics2.ui.model.CredentialModel
+import com.android.settings.password.ChooseLockGeneric
+import com.android.settings.password.ChooseLockPattern
+import com.android.settings.password.ChooseLockSettingsHelper
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+/**
+ * AutoCredentialViewModel which uses CredentialModel to determine next actions for activity, like
+ * start ChooseLockActivity, start ConfirmLockActivity, GenerateCredential, or do nothing.
+ */
+class AutoCredentialViewModel(
+    application: Application,
+    private val lockPatternUtils: LockPatternUtils,
+    private val challengeGenerator: ChallengeGenerator,
+    private val credentialModel: CredentialModel
+) : AndroidViewModel(application) {
+
+    /**
+     * Generic callback for FingerprintManager#generateChallenge or FaceManager#generateChallenge
+     */
+    interface GenerateChallengeCallback {
+        /** Generic generateChallenge method for FingerprintManager or FaceManager */
+        fun onChallengeGenerated(sensorId: Int, userId: Int, challenge: Long)
+    }
+
+    /**
+     * A generic interface class for calling different generateChallenge from FingerprintManager or
+     * FaceManager
+     */
+    interface ChallengeGenerator {
+
+        /** Get callback that will be called later after challenge generated */
+        fun getCallback(): GenerateChallengeCallback?
+
+        /** Set callback that will be called later after challenge generated */
+        fun setCallback(callback: GenerateChallengeCallback?)
+
+        /** Method for generating challenge from FingerprintManager or FaceManager */
+        fun generateChallenge(userId: Int)
+    }
+
+    /** Used to generate challenge through FingerprintRepository */
+    class FingerprintChallengeGenerator(
+        private val fingerprintRepository: FingerprintRepository
+    ) : ChallengeGenerator {
+
+        private var mCallback: GenerateChallengeCallback? = null
+
+        override fun getCallback(): GenerateChallengeCallback? {
+            return mCallback
+        }
+
+        override fun setCallback(callback: GenerateChallengeCallback?) {
+            mCallback = callback
+        }
+
+        override fun generateChallenge(userId: Int) {
+            val callback = mCallback
+            if (callback == null) {
+                Log.e(TAG, "generateChallenge, null callback")
+                return
+            }
+
+            fingerprintRepository.generateChallenge(userId) {
+                sensorId: Int, uid: Int, challenge: Long ->
+                callback.onChallengeGenerated(
+                    sensorId,
+                    uid,
+                    challenge
+                )
+            }
+        }
+
+        companion object {
+            private const val TAG = "FingerprintChallengeGenerator"
+        }
+    }
+
+    private val _generateChallengeFailedFlow = MutableSharedFlow<Boolean>()
+    val generateChallengeFailedFlow: SharedFlow<Boolean>
+        get() = _generateChallengeFailedFlow.asSharedFlow()
+
+
+    // flag if token is generating through checkCredential()'s generateChallenge()
+    private var isGeneratingChallengeDuringCheckingCredential = false
+
+    /** Get bundle which passing back to FingerprintSettings for late generateChallenge() */
+    fun createGeneratingChallengeExtras(): Bundle? {
+        if (!isGeneratingChallengeDuringCheckingCredential
+            || !credentialModel.isValidToken
+            || !credentialModel.isValidChallenge
+        ) {
+            return null
+        }
+        val bundle = Bundle()
+        bundle.putByteArray(
+            ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
+            credentialModel.token
+        )
+        bundle.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, credentialModel.challenge)
+        return bundle
+    }
+
+    /** Check credential status for biometric enrollment. */
+    fun checkCredential(scope: CoroutineScope): CredentialAction {
+        return if (isValidCredential) {
+            CredentialAction.CREDENTIAL_VALID
+        } else if (isUnspecifiedPassword) {
+            CredentialAction.FAIL_NEED_TO_CHOOSE_LOCK
+        } else if (credentialModel.isValidGkPwHandle) {
+            val gkPwHandle = credentialModel.gkPwHandle
+            credentialModel.clearGkPwHandle()
+            // GkPwHandle is got through caller activity, we shall not revoke it after
+            // generateChallenge(). Let caller activity to make decision.
+            generateChallenge(gkPwHandle, false, scope)
+            isGeneratingChallengeDuringCheckingCredential = true
+            CredentialAction.IS_GENERATING_CHALLENGE
+        } else {
+            CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK
+        }
+    }
+
+    private fun generateChallenge(
+        gkPwHandle: Long,
+        revokeGkPwHandle: Boolean,
+        scope: CoroutineScope
+    ) {
+        challengeGenerator.setCallback(object : GenerateChallengeCallback {
+            override fun onChallengeGenerated(sensorId: Int, userId: Int, challenge: Long) {
+                var illegalStateExceptionCaught = false
+                try {
+                    val newToken = requestGatekeeperHat(gkPwHandle, challenge, userId)
+                    credentialModel.challenge = challenge
+                    credentialModel.token = newToken
+                } catch (e: IllegalStateException) {
+                    Log.e(TAG, "generateChallenge, IllegalStateException", e)
+                    illegalStateExceptionCaught = true
+                } finally {
+                    if (revokeGkPwHandle) {
+                        lockPatternUtils.removeGatekeeperPasswordHandle(gkPwHandle)
+                    }
+                    Log.d(
+                        TAG,
+                        "generateChallenge(), model:$credentialModel"
+                                + ", revokeGkPwHandle:$revokeGkPwHandle"
+                    )
+                    // Check credential again
+                    if (!isValidCredential || illegalStateExceptionCaught) {
+                        Log.w(TAG, "generateChallenge, invalid Credential or IllegalStateException")
+                        scope.launch {
+                            _generateChallengeFailedFlow.emit(true)
+                        }
+                    }
+                }
+            }
+        })
+        challengeGenerator.generateChallenge(userId)
+    }
+
+    private val isValidCredential: Boolean
+        get() = !isUnspecifiedPassword && credentialModel.isValidToken
+
+    private val isUnspecifiedPassword: Boolean
+        get() = lockPatternUtils.getActivePasswordQuality(userId) == PASSWORD_QUALITY_UNSPECIFIED
+
+    /**
+     * Handle activity result from ChooseLockGeneric, ConfirmLockPassword, or ConfirmLockPattern
+     * @param isChooseLock true if result is coming from ChooseLockGeneric. False if result is
+     * coming from ConfirmLockPassword or ConfirmLockPattern
+     * @param result activity result
+     * @return if it is a valid result and viewModel is generating challenge
+     */
+    fun generateChallengeAsCredentialActivityResult(
+        isChooseLock: Boolean,
+        result: ActivityResult,
+        scope: CoroutineScope
+    ): Boolean {
+        if ((isChooseLock && result.resultCode == ChooseLockPattern.RESULT_FINISHED) ||
+            (!isChooseLock && result.resultCode == Activity.RESULT_OK)) {
+            result.data?.let {
+                val gkPwHandle = it.getLongExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
+                    CredentialModel.INVALID_GK_PW_HANDLE
+                )
+                // Revoke self requested GkPwHandle because it shall only used once inside this
+                // activity lifecycle.
+                generateChallenge(gkPwHandle, true, scope)
+                return true
+            }
+        }
+        return false
+    }
+
+    val userId: Int
+        get() = credentialModel.userId
+
+    val token: ByteArray?
+        get() = credentialModel.token
+
+    @Throws(IllegalStateException::class)
+    private fun requestGatekeeperHat(gkPwHandle: Long, challenge: Long, userId: Int): ByteArray? {
+        val response = lockPatternUtils
+            .verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId)
+        if (!response.isMatched) {
+            throw GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT")
+        }
+        return response.gatekeeperHAT
+    }
+
+    /** Create Intent for choosing lock */
+    fun createChooseLockIntent(
+        context: Context, isSuw: Boolean,
+        suwExtras: Bundle
+    ): Intent {
+        val intent = BiometricUtils.getChooseLockIntent(
+            context, isSuw,
+            suwExtras
+        )
+        intent.putExtra(
+            ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS,
+            true
+        )
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true)
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true)
+        if (credentialModel.isValidUserId) {
+            intent.putExtra(Intent.EXTRA_USER_ID, credentialModel.userId)
+        }
+        return intent
+    }
+
+    /** Create ConfirmLockLauncher */
+    fun createConfirmLockLauncher(
+        activity: Activity,
+        requestCode: Int, title: String
+    ): ChooseLockSettingsHelper {
+        val builder = ChooseLockSettingsHelper.Builder(activity)
+        builder.setRequestCode(requestCode)
+            .setTitle(title)
+            .setRequestGatekeeperPasswordHandle(true)
+            .setForegroundOnly(true)
+            .setReturnCredentials(true)
+        if (credentialModel.isValidUserId) {
+            builder.setUserId(credentialModel.userId)
+        }
+        return builder.build()
+    }
+
+    companion object {
+        private const val TAG = "AutoCredentialViewModel"
+    }
+}
+
+enum class CredentialAction {
+
+    CREDENTIAL_VALID,
+
+    /** Valid credential, activity does nothing. */
+    IS_GENERATING_CHALLENGE,
+
+    /** This credential looks good, but still need to run generateChallenge(). */
+    FAIL_NEED_TO_CHOOSE_LOCK,
+
+    /** Need activity to run confirm lock */
+    FAIL_NEED_TO_CONFIRM_LOCK
+}
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt
index 33e1bb6..37b0052 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt
@@ -23,8 +23,6 @@
 import android.util.Log
 import androidx.activity.result.ActivityResult
 import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
 import com.android.settings.biometrics.BiometricEnrollBase
 import com.android.settings.biometrics.fingerprint.FingerprintEnrollFinish.FINGERPRINT_SUGGESTION_ACTIVITY
 import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction
@@ -32,6 +30,11 @@
 import com.android.settings.biometrics2.ui.model.EnrollmentRequest
 import kotlinx.atomicfu.AtomicBoolean
 import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
 
 /**
  * Fingerprint enrollment view model implementation
@@ -44,9 +47,9 @@
 
     val isWaitingActivityResult: AtomicBoolean = atomic(false)
 
-    private val _setResultLiveData = MutableLiveData<ActivityResult>()
-    val setResultLiveData: LiveData<ActivityResult>
-        get() = _setResultLiveData
+    private val _setResultFlow = MutableSharedFlow<ActivityResult>()
+    val setResultFlow: SharedFlow<ActivityResult>
+        get() = _setResultFlow.asSharedFlow()
 
     var isNewFingerprintAdded = false
         set(value) {
@@ -94,16 +97,17 @@
      */
     fun checkFinishActivityDuringOnPause(
         isActivityFinishing: Boolean,
-        isChangingConfigurations: Boolean
+        isChangingConfigurations: Boolean,
+        scope: CoroutineScope
     ) {
         if (isChangingConfigurations || isActivityFinishing || request.isSuw
             || isWaitingActivityResult.value
         ) {
             return
         }
-        _setResultLiveData.postValue(
-            ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null)
-        )
+        scope.launch {
+            _setResultFlow.emit(ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null))
+        }
     }
 
     /**
@@ -133,23 +137,23 @@
      * Update FINGERPRINT_SUGGESTION_ACTIVITY into package manager
      */
     fun updateFingerprintSuggestionEnableState(userId: Int) {
-        val enrolled = fingerprintRepository.getNumOfEnrolledFingerprintsSize(userId)
         // Only show "Add another fingerprint" if the user already enrolled one.
         // "Add fingerprint" will be shown in the main flow if the user hasn't enrolled any
         // fingerprints. If the user already added more than one fingerprint, they already know
         // to add multiple fingerprints so we don't show the suggestion.
+        val state = if (fingerprintRepository.getNumOfEnrolledFingerprintsSize(userId) == 1)
+            PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+        else
+            PackageManager.COMPONENT_ENABLED_STATE_DISABLED
         getApplication<Application>().packageManager.setComponentEnabledSetting(
             ComponentName(
                 getApplication(),
                 FINGERPRINT_SUGGESTION_ACTIVITY
             ),
-            if (enrolled == 1)
-                PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-            else
-                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+            state,
             PackageManager.DONT_KILL_APP
         )
-        Log.d(TAG, "$FINGERPRINT_SUGGESTION_ACTIVITY enabled state = ${enrolled == 1}")
+        Log.d(TAG, "$FINGERPRINT_SUGGESTION_ACTIVITY enabled state: $state")
     }
 
     companion object {
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/model/CredentialModelTest.kt b/tests/unit/src/com/android/settings/biometrics2/ui/model/CredentialModelTest.kt
index d718db6..ac62232 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/model/CredentialModelTest.kt
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/model/CredentialModelTest.kt
@@ -38,22 +38,6 @@
         Truth.assertThat(credentialModel.userId).isEqualTo(UserHandle.myUserId())
     }
 
-    @Test
-    fun testSameValueFromBundle() {
-        val bundle = newCredentialModelIntentExtras(1234, 6677L, byteArrayOf(33, 44, 55), 987654321)
-        val model1 = CredentialModel(bundle, clock)
-        val model2 = CredentialModel(model1.bundle, clock)
-        verifySameCredentialModels(model1, model2)
-    }
-
-    @Test
-    fun testSameValueFromBundle_nullToken() {
-        val bundle = newCredentialModelIntentExtras(22, 33L, null, 21L)
-        val model1 = CredentialModel(bundle, clock)
-        val model2 = CredentialModel(model1.bundle, clock)
-        verifySameCredentialModels(model1, model2)
-    }
-
     companion object {
         @JvmStatic
         fun newCredentialModelIntentExtras(
@@ -148,36 +132,5 @@
                 }
             }
         }
-
-        fun verifySameCredentialModels(
-            model1: CredentialModel,
-            model2: CredentialModel
-        ) {
-            Truth.assertThat(model1.userId).isEqualTo(model2.userId)
-            Truth.assertThat(model1.challenge).isEqualTo(model2.challenge)
-            Truth.assertThat(model1.gkPwHandle).isEqualTo(model2.gkPwHandle)
-            val token1 = model1.token
-            val token2 = model2.token
-            if (token1 == null) {
-                Truth.assertThat(token2).isNull()
-            } else {
-                Truth.assertThat(token2).isNotNull()
-                Truth.assertThat(token1.size).isEqualTo(token2!!.size)
-                for (i in token1.indices) {
-                    Truth.assertThat(token1[i]).isEqualTo(
-                        token2[i]
-                    )
-                }
-            }
-            val bundle1 = model1.bundle
-            val bundle2 = model2.bundle
-            val keySet1 = bundle1.keySet()
-            Truth.assertThat(keySet1 == bundle2.keySet()).isTrue()
-            checkBundleIntValue(bundle1, bundle2, Intent.EXTRA_USER_ID)
-            checkBundleIntValue(bundle1, bundle2, BiometricEnrollBase.EXTRA_KEY_SENSOR_ID)
-            checkBundleLongValue(bundle1, bundle2, BiometricEnrollBase.EXTRA_KEY_CHALLENGE)
-            checkBundleByteArrayValue(bundle1, bundle2, BiometricEnrollBase.EXTRA_KEY_CHALLENGE)
-            checkBundleLongValue(bundle1, bundle2, ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE)
-        }
     }
 }
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.java
deleted file mode 100644
index 05a7239..0000000
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.java
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.biometrics2.ui.viewmodel;
-
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
-import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
-import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_CHALLENGE;
-import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_GK_PW_HANDLE;
-import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newCredentialModelIntentExtras;
-import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newGkPwHandleCredentialIntentExtras;
-import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newOnlySensorValidCredentialIntentExtras;
-import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newValidTokenCredentialIntentExtras;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CredentialAction;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.GenerateChallengeCallback;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_CREDENTIAL_MODEL;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL;
-import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN;
-import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-import androidx.activity.result.ActivityResult;
-import androidx.annotation.Nullable;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.settings.password.ChooseLockPattern;
-import com.android.settings.testutils.InstantTaskExecutorRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-@RunWith(AndroidJUnit4.class)
-public class AutoCredentialViewModelTest {
-
-    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
-    @Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
-
-    @Mock private LockPatternUtils mLockPatternUtils;
-    private TestChallengeGenerator mChallengeGenerator = null;
-    private AutoCredentialViewModel mViewModel;
-
-    @Before
-    public void setUp() {
-        mChallengeGenerator = new TestChallengeGenerator();
-        mViewModel = new AutoCredentialViewModel(
-                ApplicationProvider.getApplicationContext(),
-                mLockPatternUtils,
-                mChallengeGenerator);
-    }
-
-    private void setupGenerateChallenge(int userId, int newSensorId, long newChallenge) {
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_SOMETHING);
-        mChallengeGenerator.mUserId = userId;
-        mChallengeGenerator.mSensorId = newSensorId;
-        mChallengeGenerator.mChallenge = newChallenge;
-    }
-
-    @Test
-    public void testSetCredentialModel_sameResultFromSavedInstanceOrIntent() {
-        final Bundle extras = newCredentialModelIntentExtras(12, 33, new byte[] { 2, 3 }, 3L);
-
-        AutoCredentialViewModel viewModel2 = new AutoCredentialViewModel(
-                ApplicationProvider.getApplicationContext(),
-                mLockPatternUtils,
-                mChallengeGenerator);
-
-        mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
-        final Bundle savedInstance = new Bundle();
-        mViewModel.onSaveInstanceState(savedInstance);
-        viewModel2.setCredentialModel(savedInstance, new Intent());
-
-        assertThat(mViewModel.getUserId()).isEqualTo(viewModel2.getUserId());
-        final byte[] token1 = mViewModel.getToken();
-        final byte[] token2 = viewModel2.getToken();
-        assertThat(token1).isNotNull();
-        assertThat(token2).isNotNull();
-        assertThat(token1.length).isEqualTo(token2.length);
-        for (int i = 0; i < token2.length; ++i) {
-            assertThat(token1[i]).isEqualTo(token2[i]);
-        }
-    }
-
-    @Test
-    public void testSetCredentialModel_sameResultFromSavedInstanceOrIntent_invalidValues() {
-        final Bundle extras = newCredentialModelIntentExtras(UserHandle.USER_NULL,
-                INVALID_CHALLENGE, null, INVALID_GK_PW_HANDLE);
-
-        AutoCredentialViewModel viewModel2 = new AutoCredentialViewModel(
-                ApplicationProvider.getApplicationContext(),
-                mLockPatternUtils,
-                mChallengeGenerator);
-
-        mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
-        final Bundle savedInstance = new Bundle();
-        mViewModel.onSaveInstanceState(savedInstance);
-        viewModel2.setCredentialModel(savedInstance, new Intent());
-
-        assertThat(mViewModel.getUserId()).isEqualTo(UserHandle.USER_NULL);
-        assertThat(viewModel2.getUserId()).isEqualTo(UserHandle.USER_NULL);
-        assertThat(mViewModel.getToken()).isNull();
-        assertThat(viewModel2.getToken()).isNull();
-    }
-
-    @Test
-    public void testCheckCredential_validCredentialCase() {
-        final int userId = 99;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newValidTokenCredentialIntentExtras(userId)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_SOMETHING);
-
-        // Run credential check
-        @CredentialAction final int action = mViewModel.checkCredential();
-
-        // Check viewModel behavior
-        assertThat(action).isEqualTo(CREDENTIAL_VALID);
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-
-        // Check onSaveInstanceState()
-        final Bundle actualBundle = new Bundle();
-        mViewModel.onSaveInstanceState(actualBundle);
-        assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
-                .isFalse();
-    }
-
-    @Test
-    public void testCheckCredential_needToChooseLock() {
-        final int userId = 100;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_UNSPECIFIED);
-
-        // Run credential check
-        @CredentialAction final int action = mViewModel.checkCredential();
-
-        // Check viewModel behavior
-        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-
-        // Check onSaveInstanceState()
-        final Bundle actualBundle = new Bundle();
-        mViewModel.onSaveInstanceState(actualBundle);
-        assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
-                .isFalse();
-    }
-
-    @Test
-    public void testCheckCredential_needToConfirmLockForSomething() {
-        final int userId = 101;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_SOMETHING);
-
-        // Run credential check
-        @CredentialAction final int action = mViewModel.checkCredential();
-
-        // Check viewModel behavior
-        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-
-        // Check onSaveInstanceState()
-        final Bundle actualBundle = new Bundle();
-        mViewModel.onSaveInstanceState(actualBundle);
-        assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
-                .isFalse();
-    }
-
-    @Test
-    public void testCheckCredential_needToConfirmLockForNumeric() {
-        final int userId = 102;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_NUMERIC);
-
-        // Run credential check
-        @CredentialAction final int action = mViewModel.checkCredential();
-
-        // Check viewModel behavior
-        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-
-        // Check onSaveInstanceState()
-        final Bundle actualBundle = new Bundle();
-        mViewModel.onSaveInstanceState(actualBundle);
-        assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
-                .isFalse();
-    }
-
-    @Test
-    public void testCheckCredential_needToConfirmLockForAlphabetic() {
-        final int userId = 103;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_ALPHABETIC);
-
-        // Run credential check
-        @CredentialAction final int action = mViewModel.checkCredential();
-
-        // Check viewModel behavior
-        assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-
-        // Check onSaveInstanceState()
-        final Bundle actualBundle = new Bundle();
-        mViewModel.onSaveInstanceState(actualBundle);
-        assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
-                .isFalse();
-    }
-
-    @Test
-    public void testCheckCredential_generateChallenge() {
-        final int userId = 104;
-        final long gkPwHandle = 1111L;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_SOMETHING);
-
-        final int newSensorId = 10;
-        final long newChallenge = 20L;
-        setupGenerateChallenge(userId, newSensorId, newChallenge);
-        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
-                .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
-
-        final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
-        doAnswer(invocation -> {
-            hasCalledRemoveGkPwHandle.set(true);
-            return null;
-        }).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
-
-        // Run credential check
-        @CredentialAction final int action = mViewModel.checkCredential();
-
-        // Check viewModel behavior
-        assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-
-        // Check data inside CredentialModel
-        assertThat(mViewModel.getToken()).isNotNull();
-        assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
-        assertThat(hasCalledRemoveGkPwHandle.get()).isFalse();
-
-        // Check createGeneratingChallengeExtras()
-        final Bundle generatingChallengeExtras = mViewModel.createGeneratingChallengeExtras();
-        assertThat(generatingChallengeExtras).isNotNull();
-        assertThat(generatingChallengeExtras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
-        final byte[] tokens = generatingChallengeExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
-        assertThat(tokens).isNotNull();
-        assertThat(tokens.length).isEqualTo(1);
-        assertThat(tokens[0]).isEqualTo(1);
-
-        // Check onSaveInstanceState()
-        final Bundle actualBundle = new Bundle();
-        mViewModel.onSaveInstanceState(actualBundle);
-        assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
-                .isTrue();
-    }
-
-    @Test
-    public void testCheckCredential_generateChallengeFail() {
-        final int userId = 104;
-        final long gkPwHandle = 1111L;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_SOMETHING);
-
-        final int newSensorId = 10;
-        final long newChallenge = 20L;
-        setupGenerateChallenge(userId, newSensorId, newChallenge);
-        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
-                .thenReturn(newBadCredential(0));
-
-        // Run credential check
-        @CredentialAction final int action = mViewModel.checkCredential();
-
-        assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isTrue();
-        assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-
-        // Check onSaveInstanceState()
-        final Bundle actualBundle = new Bundle();
-        mViewModel.onSaveInstanceState(actualBundle);
-        assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
-                .isTrue();
-    }
-
-    @Test
-    public void testGetUserId_fromIntent() {
-        final int userId = 106;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-
-        // Get userId
-        assertThat(mViewModel.getUserId()).isEqualTo(userId);
-    }
-
-    @Test
-    public void testGetUserId_fromSavedInstance() {
-        final int userId = 106;
-        final Bundle savedInstance = new Bundle();
-        savedInstance.putBundle(KEY_CREDENTIAL_MODEL,
-                newOnlySensorValidCredentialIntentExtras(userId));
-        mViewModel.setCredentialModel(savedInstance, new Intent());
-
-        // Get userId
-        assertThat(mViewModel.getUserId()).isEqualTo(userId);
-    }
-
-    @Test
-    public void testCreateGeneratingChallengeExtras_generateChallenge() {
-        final Bundle credentialExtras = newValidTokenCredentialIntentExtras(200);
-        final Bundle savedInstance = new Bundle();
-        savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
-        savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, true);
-        mViewModel.setCredentialModel(savedInstance, new Intent());
-
-        // Check createGeneratingChallengeExtras()
-        final Bundle actualExtras = mViewModel.createGeneratingChallengeExtras();
-        assertThat(actualExtras).isNotNull();
-        assertThat(actualExtras.getLong(EXTRA_KEY_CHALLENGE))
-                .isEqualTo(credentialExtras.getLong(EXTRA_KEY_CHALLENGE));
-        final byte[] actualToken = actualExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
-        final byte[] expectedToken = credentialExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
-        assertThat(actualToken).isNotNull();
-        assertThat(expectedToken).isNotNull();
-        assertThat(actualToken.length).isEqualTo(expectedToken.length);
-        for (int i = 0; i < actualToken.length; ++i) {
-            assertWithMessage("tokens[" + i + "] not match").that(actualToken[i])
-                    .isEqualTo(expectedToken[i]);
-        }
-    }
-
-    @Test
-    public void testCreateGeneratingChallengeExtras_notGenerateChallenge() {
-        final Bundle credentialExtras = newValidTokenCredentialIntentExtras(201);
-        final Bundle savedInstance = new Bundle();
-        savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
-        savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, false);
-        mViewModel.setCredentialModel(savedInstance, new Intent());
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-    }
-
-    @Test
-    public void testCreateGeneratingChallengeExtras_invalidToken() {
-        final Bundle credentialExtras = newOnlySensorValidCredentialIntentExtras(202);
-        final Bundle savedInstance = new Bundle();
-        savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
-        savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, true);
-        mViewModel.setCredentialModel(savedInstance, new Intent());
-
-        // Check createGeneratingChallengeExtras()
-        assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
-    }
-
-    @Test
-    public void testCheckNewCredentialFromActivityResult_invalidChooseLock() {
-        final int userId = 107;
-        final long gkPwHandle = 3333L;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
-        final Intent intent = new Intent();
-        intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
-
-        // run checkNewCredentialFromActivityResult()
-        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
-                new ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent));
-
-        assertThat(ret).isFalse();
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-    }
-
-    @Test
-    public void testCheckNewCredentialFromActivityResult_invalidConfirmLock() {
-        final int userId = 107;
-        final long gkPwHandle = 3333L;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
-        final Intent intent = new Intent();
-        intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
-
-        // run checkNewCredentialFromActivityResult()
-        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
-                new ActivityResult(Activity.RESULT_OK + 1, intent));
-
-        assertThat(ret).isFalse();
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-    }
-
-    @Test
-    public void testCheckNewCredentialFromActivityResult_nullDataChooseLock() {
-        final int userId = 108;
-        final long gkPwHandle = 4444L;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
-
-        // run checkNewCredentialFromActivityResult()
-        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
-                new ActivityResult(ChooseLockPattern.RESULT_FINISHED, null));
-
-        assertThat(ret).isFalse();
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-    }
-
-    @Test
-    public void testCheckNewCredentialFromActivityResult_nullDataConfirmLock() {
-        final int userId = 109;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-
-        // run checkNewCredentialFromActivityResult()
-        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
-                new ActivityResult(Activity.RESULT_OK, null));
-
-        assertThat(ret).isFalse();
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-    }
-
-    @Test
-    public void testCheckNewCredentialFromActivityResult_validChooseLock() {
-        final int userId = 108;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_SOMETHING);
-
-        final long gkPwHandle = 6666L;
-        final int newSensorId = 50;
-        final long newChallenge = 60L;
-        setupGenerateChallenge(userId, newSensorId, newChallenge);
-        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
-                .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
-
-        final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
-        doAnswer(invocation -> {
-            hasCalledRemoveGkPwHandle.set(true);
-            return null;
-        }).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
-
-        // Run checkNewCredentialFromActivityResult()
-        final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
-        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
-                new ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent));
-
-        assertThat(ret).isTrue();
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-        assertThat(mViewModel.getToken()).isNotNull();
-        assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
-        assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
-    }
-
-    @Test
-    public void testCheckNewCredentialFromActivityResult_validConfirmLock() {
-        final int userId = 109;
-        mViewModel.setCredentialModel(null,
-                new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
-        when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
-                PASSWORD_QUALITY_SOMETHING);
-
-        final long gkPwHandle = 5555L;
-        final int newSensorId = 80;
-        final long newChallenge = 90L;
-        setupGenerateChallenge(userId, newSensorId, newChallenge);
-        when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
-                .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
-
-        final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
-        doAnswer(invocation -> {
-            hasCalledRemoveGkPwHandle.set(true);
-            return null;
-        }).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
-
-        // Run checkNewCredentialFromActivityResult()
-        final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
-        final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
-                new ActivityResult(Activity.RESULT_OK, intent));
-
-        assertThat(ret).isTrue();
-        assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
-        assertThat(mViewModel.getToken()).isNotNull();
-        assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
-        assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
-    }
-
-    public static class TestChallengeGenerator implements ChallengeGenerator {
-        public int mSensorId = -1;
-        public int mUserId = UserHandle.myUserId();
-        public long mChallenge = INVALID_CHALLENGE;
-        public int mCallbackRunCount = 0;
-        private GenerateChallengeCallback mCallback;
-
-        @Nullable
-        @Override
-        public GenerateChallengeCallback getCallback() {
-            return mCallback;
-        }
-
-        @Override
-        public void setCallback(@Nullable GenerateChallengeCallback callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void generateChallenge(int userId) {
-            final GenerateChallengeCallback callback = mCallback;
-            if (callback == null) {
-                return;
-            }
-            callback.onChallengeGenerated(mSensorId, mUserId, mChallenge);
-            ++mCallbackRunCount;
-        }
-    }
-
-    private VerifyCredentialResponse newGoodCredential(long gkPwHandle, @NonNull byte[] hat) {
-        return new VerifyCredentialResponse.Builder()
-                .setGatekeeperPasswordHandle(gkPwHandle)
-                .setGatekeeperHAT(hat)
-                .build();
-    }
-
-    private VerifyCredentialResponse newBadCredential(int timeout) {
-        if (timeout > 0) {
-            return VerifyCredentialResponse.fromTimeout(timeout);
-        } else {
-            return VerifyCredentialResponse.fromError();
-        }
-    }
-}
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.kt b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.kt
new file mode 100644
index 0000000..3ae4951
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModelTest.kt
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.biometrics2.ui.viewmodel
+
+import android.app.Activity
+import android.app.admin.DevicePolicyManager
+import android.content.Intent
+import android.os.Bundle
+import android.os.SystemClock
+import android.os.UserHandle
+import androidx.activity.result.ActivityResult
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.VerifyCredentialResponse
+import com.android.settings.biometrics.BiometricEnrollBase
+import com.android.settings.biometrics2.ui.model.CredentialModel
+import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newGkPwHandleCredentialIntentExtras
+import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newOnlySensorValidCredentialIntentExtras
+import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newValidTokenCredentialIntentExtras
+import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator
+import com.android.settings.password.ChooseLockPattern
+import com.android.settings.password.ChooseLockSettingsHelper
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import java.util.concurrent.atomic.AtomicBoolean
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AutoCredentialViewModelTest {
+
+    @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var lockPatternUtils: LockPatternUtils
+
+    private var challengeGenerator: TestChallengeGenerator = TestChallengeGenerator()
+
+    private lateinit var viewModel: AutoCredentialViewModel
+    private fun newAutoCredentialViewModel(bundle: Bundle?): AutoCredentialViewModel {
+        return AutoCredentialViewModel(
+            ApplicationProvider.getApplicationContext(),
+            lockPatternUtils,
+            challengeGenerator,
+            CredentialModel(bundle, SystemClock.elapsedRealtimeClock())
+        )
+    }
+
+    @Before
+    fun setUp() {
+        challengeGenerator = TestChallengeGenerator()
+    }
+
+    private fun setupGenerateChallenge(userId: Int, newSensorId: Int, newChallenge: Long) {
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+        )
+        challengeGenerator.userId = userId
+        challengeGenerator.sensorId = newSensorId
+        challengeGenerator.challenge = newChallenge
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckCredential_validCredentialCase() = runTest {
+        val userId = 99
+        viewModel = newAutoCredentialViewModel(newValidTokenCredentialIntentExtras(userId))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+        )
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run credential check
+        val action = viewModel.checkCredential(backgroundScope)
+        runCurrent()
+
+        // Check viewModel behavior
+        assertThat(action).isEqualTo(CredentialAction.CREDENTIAL_VALID)
+        assertThat(generateFails.size).isEqualTo(0)
+
+        // Check createGeneratingChallengeExtras()
+        assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckCredential_needToChooseLock() = runTest {
+        val userId = 100
+        viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
+        )
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run credential check
+        val action = viewModel.checkCredential(backgroundScope)
+        runCurrent()
+
+        // Check viewModel behavior
+        assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CHOOSE_LOCK)
+        assertThat(generateFails.size).isEqualTo(0)
+
+        // Check createGeneratingChallengeExtras()
+        assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckCredential_needToConfirmLockForSomething() = runTest {
+        val userId = 101
+        viewModel =
+            newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+        )
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run credential check
+        val action = viewModel.checkCredential(backgroundScope)
+        runCurrent()
+
+        // Check viewModel behavior
+        assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
+        assertThat(generateFails.size).isEqualTo(0)
+
+        // Check createGeneratingChallengeExtras()
+        assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckCredential_needToConfirmLockForNumeric() = runTest {
+        val userId = 102
+        viewModel =
+            newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+        )
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run credential check
+        val action = viewModel.checkCredential(backgroundScope)
+        runCurrent()
+
+        // Check viewModel behavior
+        assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
+        assertThat(generateFails.size).isEqualTo(0)
+
+        // Check createGeneratingChallengeExtras()
+        assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckCredential_needToConfirmLockForAlphabetic() = runTest {
+        val userId = 103
+        viewModel =
+            newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
+        )
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run credential check
+        val action = viewModel.checkCredential(this)
+        runCurrent()
+
+        // Check viewModel behavior
+        assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
+        assertThat(generateFails.size).isEqualTo(0)
+
+        // Check createGeneratingChallengeExtras()
+        assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckCredential_generateChallenge() = runTest {
+        val userId = 104
+        val gkPwHandle = 1111L
+        viewModel =
+            newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+        )
+        val newSensorId = 10
+        val newChallenge = 20L
+        setupGenerateChallenge(userId, newSensorId, newChallenge)
+        whenever(
+            lockPatternUtils.verifyGatekeeperPasswordHandle(
+                gkPwHandle,
+                newChallenge,
+                userId
+            )
+        )
+            .thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
+        val hasCalledRemoveGkPwHandle = AtomicBoolean()
+        Mockito.doAnswer {
+            hasCalledRemoveGkPwHandle.set(true)
+            null
+        }.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run credential check
+        val action = viewModel.checkCredential(backgroundScope)
+        runCurrent()
+
+        // Check viewModel behavior
+        assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
+        assertThat(generateFails.size).isEqualTo(0)
+
+        // Check data inside CredentialModel
+        assertThat(viewModel.token).isNotNull()
+        assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
+        assertThat(hasCalledRemoveGkPwHandle.get()).isFalse()
+
+        // Check createGeneratingChallengeExtras()
+        val generatingChallengeExtras = viewModel.createGeneratingChallengeExtras()
+        assertThat(generatingChallengeExtras).isNotNull()
+        assertThat(generatingChallengeExtras!!.getLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE))
+            .isEqualTo(newChallenge)
+        val tokens =
+            generatingChallengeExtras.getByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
+        assertThat(tokens).isNotNull()
+        assertThat(tokens!!.size).isEqualTo(1)
+        assertThat(tokens[0]).isEqualTo(1)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckCredential_generateChallengeFail() = runTest {
+        backgroundScope.launch {
+            val userId = 104
+            val gkPwHandle = 1111L
+            viewModel =
+                newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
+            whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+                DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+            )
+            val newSensorId = 10
+            val newChallenge = 20L
+            setupGenerateChallenge(userId, newSensorId, newChallenge)
+            whenever(
+                lockPatternUtils.verifyGatekeeperPasswordHandle(
+                    gkPwHandle,
+                    newChallenge,
+                    userId
+                )
+            )
+                .thenReturn(newBadCredential(0))
+
+            val generateFails = listOfGenerateChallengeFailedFlow()
+
+            // Run credential check
+            val action = viewModel.checkCredential(this)
+            runCurrent()
+
+            assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
+            assertThat(generateFails.size).isEqualTo(1)
+            assertThat(generateFails[0]).isEqualTo(true)
+            assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
+
+            // Check createGeneratingChallengeExtras()
+            assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
+        }
+    }
+
+    @Test
+    fun testGetUserId_fromIntent() {
+        val userId = 106
+        viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+
+        // Get userId
+        assertThat(viewModel.userId).isEqualTo(userId)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGenerateChallengeAsCredentialActivityResult_invalidChooseLock() = runTest {
+        backgroundScope.launch {
+            val userId = 107
+            val gkPwHandle = 3333L
+            viewModel =
+                newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
+            val intent = Intent()
+            intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
+
+            val generateFails = listOfGenerateChallengeFailedFlow()
+
+            // Run generateChallengeAsCredentialActivityResult()
+            val ret = viewModel.generateChallengeAsCredentialActivityResult(
+                true,
+                ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent),
+                backgroundScope
+            )
+            runCurrent()
+
+            assertThat(ret).isFalse()
+            assertThat(generateFails.size).isEqualTo(0)
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGenerateChallengeAsCredentialActivityResult_invalidConfirmLock() = runTest {
+        backgroundScope.launch {
+            val userId = 107
+            val gkPwHandle = 3333L
+            viewModel =
+                newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
+            val intent = Intent()
+            intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
+
+            val generateFails = listOfGenerateChallengeFailedFlow()
+
+            // Run generateChallengeAsCredentialActivityResult()
+            val ret = viewModel.generateChallengeAsCredentialActivityResult(
+                false,
+                ActivityResult(Activity.RESULT_OK + 1, intent),
+                backgroundScope
+            )
+            runCurrent()
+
+            assertThat(ret).isFalse()
+            assertThat(generateFails.size).isEqualTo(0)
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGenerateChallengeAsCredentialActivityResult_nullDataChooseLock() = runTest {
+        val userId = 108
+        val gkPwHandle = 4444L
+        viewModel =
+            newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run generateChallengeAsCredentialActivityResult()
+        val ret = viewModel.generateChallengeAsCredentialActivityResult(
+            true,
+            ActivityResult(ChooseLockPattern.RESULT_FINISHED, null),
+            backgroundScope
+        )
+        runCurrent()
+
+        assertThat(ret).isFalse()
+        assertThat(generateFails.size).isEqualTo(0)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGenerateChallengeAsCredentialActivityResult_nullDataConfirmLock() = runTest {
+        val userId = 109
+        viewModel =
+            newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run generateChallengeAsCredentialActivityResult()
+        val ret = viewModel.generateChallengeAsCredentialActivityResult(
+            false,
+            ActivityResult(Activity.RESULT_OK, null),
+            backgroundScope
+        )
+        runCurrent()
+
+        assertThat(ret).isFalse()
+        assertThat(generateFails.size).isEqualTo(0)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGenerateChallengeAsCredentialActivityResult_validChooseLock() = runTest {
+        val userId = 108
+        viewModel =
+            newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+        )
+        val gkPwHandle = 6666L
+        val newSensorId = 50
+        val newChallenge = 60L
+        setupGenerateChallenge(userId, newSensorId, newChallenge)
+        whenever(
+            lockPatternUtils.verifyGatekeeperPasswordHandle(
+                gkPwHandle,
+                newChallenge,
+                userId
+            )
+        )
+            .thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
+        val hasCalledRemoveGkPwHandle = AtomicBoolean()
+        Mockito.doAnswer {
+            hasCalledRemoveGkPwHandle.set(true)
+            null
+        }.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run generateChallengeAsCredentialActivityResult()
+        val intent =
+            Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
+        val ret = viewModel.generateChallengeAsCredentialActivityResult(
+            true,
+            ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent),
+            backgroundScope
+        )
+        runCurrent()
+
+        assertThat(ret).isTrue()
+        assertThat(generateFails.size).isEqualTo(0)
+        assertThat(viewModel.token).isNotNull()
+        assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
+        assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGenerateChallengeAsCredentialActivityResult_validConfirmLock() = runTest {
+        val userId = 109
+        viewModel =
+            newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
+        whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
+            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+        )
+        val gkPwHandle = 5555L
+        val newSensorId = 80
+        val newChallenge = 90L
+        setupGenerateChallenge(userId, newSensorId, newChallenge)
+        whenever(
+            lockPatternUtils.verifyGatekeeperPasswordHandle(
+                gkPwHandle,
+                newChallenge,
+                userId
+            )
+        )
+            .thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
+        val hasCalledRemoveGkPwHandle = AtomicBoolean()
+        Mockito.doAnswer {
+            hasCalledRemoveGkPwHandle.set(true)
+            null
+        }.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
+
+        val generateFails = listOfGenerateChallengeFailedFlow()
+
+        // Run generateChallengeAsCredentialActivityResult()
+        val intent =
+            Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
+        val ret = viewModel.generateChallengeAsCredentialActivityResult(
+            false,
+            ActivityResult(Activity.RESULT_OK, intent),
+            backgroundScope
+        )
+        runCurrent()
+
+        assertThat(ret).isTrue()
+        assertThat(generateFails.size).isEqualTo(0)
+        assertThat(viewModel.token).isNotNull()
+        assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
+        assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    private fun TestScope.listOfGenerateChallengeFailedFlow(): List<Boolean> =
+        mutableListOf<Boolean>().also {
+            backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
+                viewModel.generateChallengeFailedFlow.toList(it)
+            }
+        }
+
+    class TestChallengeGenerator : ChallengeGenerator {
+        var sensorId = -1
+        var userId = UserHandle.myUserId()
+        var challenge = CredentialModel.INVALID_CHALLENGE
+        var callbackRunCount = 0
+
+        private var _callback: AutoCredentialViewModel.GenerateChallengeCallback? = null
+
+        override fun getCallback(): AutoCredentialViewModel.GenerateChallengeCallback? {
+            return _callback
+        }
+
+        override fun setCallback(callback: AutoCredentialViewModel.GenerateChallengeCallback?) {
+            _callback = callback
+        }
+
+        override fun generateChallenge(userId: Int) {
+            val callback = _callback ?: return
+            callback.onChallengeGenerated(sensorId, this.userId, challenge)
+            ++callbackRunCount
+        }
+    }
+
+    private fun newGoodCredential(gkPwHandle: Long, hat: ByteArray): VerifyCredentialResponse {
+        return VerifyCredentialResponse.Builder()
+            .setGatekeeperPasswordHandle(gkPwHandle)
+            .setGatekeeperHAT(hat)
+            .build()
+    }
+
+    private fun newBadCredential(timeout: Int): VerifyCredentialResponse {
+        return if (timeout > 0) {
+            VerifyCredentialResponse.fromTimeout(timeout)
+        } else {
+            VerifyCredentialResponse.fromError()
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt
index 9f339de..bee91c9 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt
@@ -23,12 +23,20 @@
 import androidx.activity.result.ActivityResult
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.biometrics.BiometricEnrollBase
 import com.android.settings.biometrics2.data.repository.FingerprintRepository
 import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest
+import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwRequest
 import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository
 import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints
-import com.android.settings.testutils.InstantTaskExecutorRule
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -42,8 +50,6 @@
 
     @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
 
-    @get:Rule val taskExecutorRule = InstantTaskExecutorRule()
-
     private val application: Application
         get() = ApplicationProvider.getApplicationContext()
 
@@ -69,12 +75,12 @@
 
     @Test
     fun testGetRequest() {
-        Truth.assertThat(viewModel.request).isNotNull()
+        assertThat(viewModel.request).isNotNull()
     }
 
     @Test
     fun testIsWaitingActivityResultDefaultFalse() {
-        Truth.assertThat(viewModel.isWaitingActivityResult.value).isFalse()
+        assertThat(viewModel.isWaitingActivityResult.value).isFalse()
     }
 
 
@@ -83,8 +89,8 @@
         val retResult = viewModel.getOverrideActivityResult(
             ActivityResult(22, null), null
         )
-        Truth.assertThat(retResult).isNotNull()
-        Truth.assertThat(retResult.data).isNull()
+        assertThat(retResult).isNotNull()
+        assertThat(retResult.data).isNull()
     }
 
     @Test
@@ -93,8 +99,8 @@
         val retResult = viewModel.getOverrideActivityResult(
             ActivityResult(33, intent), null
         )
-        Truth.assertThat(retResult).isNotNull()
-        Truth.assertThat(retResult.data).isEqualTo(intent)
+        assertThat(retResult).isNotNull()
+        assertThat(retResult.data).isEqualTo(intent)
     }
 
     @Test
@@ -106,8 +112,8 @@
             ActivityResult(33, null), extra
         )
 
-        Truth.assertThat(retResult).isNotNull()
-        Truth.assertThat(retResult.data).isNull()
+        assertThat(retResult).isNotNull()
+        assertThat(retResult.data).isNull()
     }
 
     @Test
@@ -124,16 +130,16 @@
         val retResult = viewModel.getOverrideActivityResult(
             ActivityResult(33, null), extra
         )
-        Truth.assertThat(retResult).isNotNull()
+        assertThat(retResult).isNotNull()
 
         val retIntent = retResult.data
-        Truth.assertThat(retIntent).isNotNull()
+        assertThat(retIntent).isNotNull()
 
         val retExtra = retIntent!!.extras
-        Truth.assertThat(retExtra).isNotNull()
-        Truth.assertThat(retExtra!!.size).isEqualTo(extra.size)
-        Truth.assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
-        Truth.assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
+        assertThat(retExtra).isNotNull()
+        assertThat(retExtra!!.size).isEqualTo(extra.size)
+        assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
+        assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
     }
 
     @Test
@@ -149,15 +155,15 @@
 
         val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra)
 
-        Truth.assertThat(retResult).isNotNull()
+        assertThat(retResult).isNotNull()
 
         val retIntent = retResult.data
-        Truth.assertThat(retIntent).isNotNull()
+        assertThat(retIntent).isNotNull()
 
         val retExtra = retIntent!!.extras
-        Truth.assertThat(retExtra).isNotNull()
-        Truth.assertThat(retExtra!!.size).isEqualTo(intent.extras!!.size)
-        Truth.assertThat(retExtra.getString(key2)).isEqualTo(intent.extras!!.getString(key2))
+        assertThat(retExtra).isNotNull()
+        assertThat(retExtra!!.size).isEqualTo(intent.extras!!.size)
+        assertThat(retExtra.getString(key2)).isEqualTo(intent.extras!!.getString(key2))
     }
 
     @Test
@@ -177,17 +183,17 @@
         viewModel.isNewFingerprintAdded = true
 
         val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra)
-        Truth.assertThat(retResult).isNotNull()
+        assertThat(retResult).isNotNull()
 
         val retIntent = retResult.data
-        Truth.assertThat(retIntent).isNotNull()
+        assertThat(retIntent).isNotNull()
 
         val retExtra = retIntent!!.extras
-        Truth.assertThat(retExtra).isNotNull()
-        Truth.assertThat(retExtra!!.size).isEqualTo(extra.size + intent.extras!!.size)
-        Truth.assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
-        Truth.assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
-        Truth.assertThat(retExtra.getLong(key3)).isEqualTo(intent.extras!!.getLong(key3))
+        assertThat(retExtra).isNotNull()
+        assertThat(retExtra!!.size).isEqualTo(extra.size + intent.extras!!.size)
+        assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
+        assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
+        assertThat(retExtra.getLong(key3)).isEqualTo(intent.extras!!.getLong(key3))
     }
 
     @Test
@@ -205,18 +211,120 @@
         )
 
         setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 0)
-        Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
+        assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
 
         setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 1)
-        Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
+        assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
 
         setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 2)
-        Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
+        assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
 
         setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 3)
-        Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
+        assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
 
         setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 4)
-        Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
+        assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
     }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testSetResultFlow_defaultEmpty() = runTest {
+        val activityResults = listOfSetResultFlow()
+
+        runCurrent()
+
+        assertThat(activityResults.size).isEqualTo(0)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckFinishActivityDuringOnPause_doNothingIfIsSuw() = runTest {
+        viewModel = FingerprintEnrollmentViewModel(
+            application,
+            fingerprintRepository,
+            newIsSuwRequest(application)
+        )
+
+        val activityResults = listOfSetResultFlow()
+
+        viewModel.checkFinishActivityDuringOnPause(
+            isActivityFinishing = false,
+            isChangingConfigurations = false,
+            scope = this
+        )
+        runCurrent()
+
+        assertThat(activityResults.size).isEqualTo(0)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckFinishActivityDuringOnPause_doNothingIfIsWaitingActivity() = runTest {
+        val activityResults = listOfSetResultFlow()
+
+        viewModel.isWaitingActivityResult.value = true
+        viewModel.checkFinishActivityDuringOnPause(
+            isActivityFinishing = false,
+            isChangingConfigurations = false,
+            scope = this
+        )
+        runCurrent()
+
+        assertThat(activityResults.size).isEqualTo(0)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckFinishActivityDuringOnPause_doNothingIfIsActivityFinishing() = runTest {
+        val activityResults = listOfSetResultFlow()
+
+        viewModel.checkFinishActivityDuringOnPause(
+            isActivityFinishing = true,
+            isChangingConfigurations = false,
+            scope = this
+        )
+        runCurrent()
+
+        assertThat(activityResults.size).isEqualTo(0)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckFinishActivityDuringOnPause_doNothingIfIsChangingConfigurations() = runTest {
+        val activityResults = listOfSetResultFlow()
+
+        viewModel.checkFinishActivityDuringOnPause(
+            isActivityFinishing = false,
+            isChangingConfigurations = true,
+            scope = this
+        )
+        runCurrent()
+
+        assertThat(activityResults.size).isEqualTo(0)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCheckFinishActivityDuringOnPause_defaultFinishSelf() = runTest {
+        val activityResults = listOfSetResultFlow()
+
+        viewModel.checkFinishActivityDuringOnPause(
+            isActivityFinishing = false,
+            isChangingConfigurations = false,
+            scope = backgroundScope
+        )
+        runCurrent()
+
+        assertThat(activityResults.size).isEqualTo(1)
+        assertThat(activityResults[0].resultCode).isEqualTo(BiometricEnrollBase.RESULT_TIMEOUT)
+        assertThat(activityResults[0].data).isEqualTo(null)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    private fun TestScope.listOfSetResultFlow(): List<ActivityResult> =
+        mutableListOf<ActivityResult>().also {
+            backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
+                viewModel.setResultFlow.toList(it)
+            }
+        }
 }