Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acb8be5d authored by Milton Wu's avatar Milton Wu
Browse files

[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
parent 78f3760d
Loading
Loading
Loading
Loading
+20 −18
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import androidx.lifecycle.viewmodel.CreationExtras;
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 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
            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 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
            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 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
        } 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 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
                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);
+0 −14
Original line number Diff line number Diff line
@@ -80,20 +80,6 @@ class CredentialModel(bundle: Bundle?, private val clock: Clock) {
    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
+44 −46
Original line number Diff line number Diff line
@@ -44,16 +44,13 @@ import com.android.settings.Utils
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 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        autoCredentialViewModel.setCredentialModel(savedInstanceState, intent)

        // Theme
        setTheme(viewModel.request.theme)
@@ -219,14 +215,23 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
            }
        }

        // 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)")
@@ -236,8 +241,6 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
                    )
                }
            }
        }
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                errorDialogViewModel.setResultFlow.collect {
                    Log.d(TAG, "errorDialogSetResultFlow($it)")
@@ -408,10 +411,6 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
        }
    }

    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 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
    }

    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 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
                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 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
                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 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {

    override fun onPause() {
        super.onPause()
        viewModel.checkFinishActivityDuringOnPause(isFinishing, isChangingConfigurations)
        viewModel.checkFinishActivityDuringOnPause(
            isFinishing,
            isChangingConfigurations,
            lifecycleScope
        )
    }

    override fun onDestroy() {
@@ -596,17 +602,14 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
    }

    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)
        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[USER_ID_KEY] = credentialModel.userId
            }
            it[CREDENTIAL_MODEL_KEY] =
                CredentialModel(intent.extras, SystemClock.elapsedRealtimeClock())
        }

    override val defaultViewModelProviderFactory: ViewModelProvider.Factory
@@ -630,11 +633,6 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
        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"
+0 −393

File deleted.

Preview size limit exceeded, changes collapsed.

+300 −0
Original line number Diff line number Diff line
/*
 * 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
}
Loading