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

Commit f5452ad8 authored by Austin Delgado's avatar Austin Delgado Committed by Android (Google) Code Review
Browse files

Merge "Support BP retry face auth on SFPS acquired" into 24D1-dev

parents db6fde82 e4117b13
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -457,6 +457,23 @@ object BiometricViewBinder {
                        }
                    }
                }

                // Retry and confirmation when finger on sensor
                launch {
                    combine(
                            viewModel.canTryAgainNow,
                            viewModel.hasFingerOnSensor,
                            viewModel.isPendingConfirmation,
                            ::Triple
                        )
                        .collect { (canRetry, fingerAcquired, pendingConfirmation) ->
                            if (canRetry && fingerAcquired) {
                                legacyCallback.onButtonTryAgain()
                            } else if (pendingConfirmation && fingerAcquired) {
                                viewModel.confirmAuthenticated()
                            }
                        }
                }
            }
        }

+22 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.pm.PackageManager
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.BiometricPrompt
import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptContentView
@@ -31,6 +32,7 @@ import android.view.MotionEvent
import com.android.systemui.Flags.bpTalkback
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -39,6 +41,7 @@ import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.Job
@@ -65,6 +68,7 @@ constructor(
    promptSelectorInteractor: PromptSelectorInteractor,
    @Application private val context: Context,
    private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
    private val biometricStatusInteractor: BiometricStatusInteractor,
    private val udfpsUtils: UdfpsUtils
) {
    /** The set of modalities available for this prompt */
@@ -147,6 +151,24 @@ constructor(
    /** Fingerprint sensor state. */
    val fingerprintStartMode: Flow<FingerprintStartMode> = _fingerprintStartMode.asStateFlow()

    /** Whether a finger has been acquired by the sensor */
    // TODO(b/331948073): Add support for detecting SFPS finger without authentication running
    val hasFingerBeenAcquired: Flow<Boolean> =
        combine(biometricStatusInteractor.fingerprintAcquiredStatus, modalities) {
                status,
                modalities ->
                modalities.hasSfps &&
                    status is AcquiredFingerprintAuthenticationStatus &&
                    status.acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
            }
            .distinctUntilChanged()

    /** Whether there is currently a finger on the sensor */
    val hasFingerOnSensor: Flow<Boolean> =
        combine(hasFingerBeenAcquired, _isOverlayTouched) { hasFingerBeenAcquired, overlayTouched ->
            hasFingerBeenAcquired || overlayTouched
        }

    private val _forceLargeSize = MutableStateFlow(false)
    private val _forceMediumSize = MutableStateFlow(false)

+10 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.systemui.biometrics

import android.app.ActivityTaskManager
import android.app.admin.DevicePolicyManager
import android.content.pm.PackageManager
import android.hardware.biometrics.BiometricAuthenticator
@@ -41,9 +42,12 @@ import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
@@ -116,10 +120,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
    lateinit var selectedUserInteractor: SelectedUserInteractor
    @Mock
    private lateinit var packageManager: PackageManager
    @Mock private lateinit var activityTaskManager: ActivityTaskManager

    private val testScope = TestScope(StandardTestDispatcher())
    private val fakeExecutor = FakeExecutor(FakeSystemClock())
    private val biometricPromptRepository = FakePromptRepository()
    private val biometricStatusRepository = FakeBiometricStatusRepository()
    private val fingerprintRepository = FakeFingerprintPropertyRepository()
    private val displayStateRepository = FakeDisplayStateRepository()
    private val credentialInteractor = FakeCredentialInteractor()
@@ -139,6 +145,7 @@ open class AuthContainerViewTest : SysuiTestCase() {
    private lateinit var displayRepository: FakeDisplayRepository
    private lateinit var displayStateInteractor: DisplayStateInteractor
    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
    private lateinit var biometricStatusInteractor: BiometricStatusInteractor

    private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
    private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
@@ -164,6 +171,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
                        selectedUserInteractor,
                        testScope.backgroundScope,
                )
        biometricStatusInteractor =
                BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
        // Set up default logo icon
        whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
        context.setMockPackageManager(packageManager)
@@ -577,6 +586,7 @@ open class AuthContainerViewTest : SysuiTestCase() {
            promptSelectorInteractor,
            context,
            udfpsOverlayInteractor,
            biometricStatusInteractor,
            udfpsUtils
        ),
        { credentialViewModel },
+33 −11
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package com.android.systemui.biometrics.ui.viewmodel

import android.app.ActivityTaskManager
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.drawable.BitmapDrawable
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
import android.hardware.biometrics.PromptContentItemBulletedText
import android.hardware.biometrics.PromptContentView
@@ -38,9 +40,12 @@ import com.android.systemui.Flags.FLAG_BP_TALKBACK
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -49,6 +54,7 @@ import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -57,6 +63,7 @@ import com.android.systemui.biometrics.shared.model.toSensorType
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
@@ -100,6 +107,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
    @Mock private lateinit var packageManager: PackageManager
    @Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
    @Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
    @Mock private lateinit var activityTaskManager: ActivityTaskManager

    private val fakeExecutor = FakeExecutor(FakeSystemClock())
    private val testScope = TestScope()
@@ -113,9 +121,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
    private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
    private lateinit var promptRepository: FakePromptRepository
    private lateinit var displayStateRepository: FakeDisplayStateRepository
    private lateinit var biometricStatusRepository: FakeBiometricStatusRepository
    private lateinit var displayRepository: FakeDisplayRepository
    private lateinit var displayStateInteractor: DisplayStateInteractor
    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
    private lateinit var biometricStatusInteractor: BiometricStatusInteractor

    private lateinit var selector: PromptSelectorInteractor
    private lateinit var viewModel: PromptViewModel
@@ -153,6 +163,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
                selectedUserInteractor,
                testScope.backgroundScope
            )
        biometricStatusRepository = FakeBiometricStatusRepository()
        biometricStatusInteractor =
            BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
        selector =
            PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
        selector.resetPrompt()
@@ -168,6 +181,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
                selector,
                mContext,
                udfpsOverlayInteractor,
                biometricStatusInteractor,
                udfpsUtils
            )
        iconViewModel = viewModel.iconViewModel
@@ -1043,8 +1057,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
    fun auto_confirm_authentication_when_finger_down() = runGenericTest {
        val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)

        // No icon button when face only, can't confirm before auth
        if (!testCase.isFaceOnly) {
        if (testCase.isCoex) {
            viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
        }
        viewModel.showAuthenticated(testCase.authenticatedModality, 0)
@@ -1059,7 +1072,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
        assertThat(canTryAgain).isFalse()
        assertThat(authenticated?.isAuthenticated).isTrue()

        if (testCase.isFaceOnly && expectConfirmation) {
        if (expectConfirmation) {
            if (testCase.isFaceOnly) {
                assertThat(size).isEqualTo(PromptSize.MEDIUM)
                assertButtonsVisible(
                    cancel = true,
@@ -1067,6 +1081,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
                )

                viewModel.confirmAuthenticated()
            } else if (testCase.isCoex) {
                assertThat(authenticated?.isAuthenticatedAndConfirmed).isTrue()
            }
            assertThat(message).isEqualTo(PromptMessage.Empty)
            assertButtonsVisible()
        }
@@ -1076,8 +1093,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
    fun cannot_auto_confirm_authentication_when_finger_up() = runGenericTest {
        val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)

        // No icon button when face only, can't confirm before auth
        if (!testCase.isFaceOnly) {
        if (testCase.isCoex) {
            viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
            viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP))
        }
@@ -1379,6 +1395,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
            packageName = packageName,
        )

        biometricStatusRepository.setFingerprintAcquiredStatus(
            AcquiredFingerprintAuthenticationStatus(
                AuthenticationReason.BiometricPromptAuthentication,
                BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
            )
        )
        // put the view model in the initial authenticating state, unless explicitly skipped
        val startMode =
            when {
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.ui.viewmodel

import android.content.applicationContext
import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
@@ -30,6 +31,7 @@ val Kosmos.promptViewModel by Fixture {
        promptSelectorInteractor = promptSelectorInteractor,
        context = applicationContext,
        udfpsOverlayInteractor = udfpsOverlayInteractor,
        biometricStatusInteractor = biometricStatusInteractor,
        udfpsUtils = udfpsUtils
    )
}