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

Commit 07222992 authored by Greg Kaiser's avatar Greg Kaiser Committed by Automerger Merge Worker
Browse files

Merge "[conflict] Merge "Fix BP face auth requiring full tap after auth" into...

Merge "[conflict] Merge "Fix BP face auth requiring full tap after auth" into udc-d1-dev am: d97c8972" into udc-qpr-dev am: 211eeaa2 am: 1ba8ee33

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/24409571



Change-Id: I8e4c7c72fbac817d0836f3c6baacba51f3067df0
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 61961e16 1ba8ee33
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -646,8 +646,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            shouldPilfer = true;
        }

        // Pilfer only once per gesture
        if (shouldPilfer && !mPointerPilfered) {
        // Pilfer only once per gesture, don't pilfer for BP
        if (shouldPilfer && !mPointerPilfered
                && getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) {
            mInputManager.pilferPointers(
                    mOverlay.getOverlayView().getViewRootImpl().getInputToken());
            mPointerPilfered = true;
+18 −9
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.ui.binder

import android.animation.Animator
import android.annotation.SuppressLint
import android.content.Context
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
@@ -26,6 +27,7 @@ import android.os.Bundle
import android.text.method.ScrollingMovementMethod
import android.util.Log
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
import android.view.accessibility.AccessibilityManager
@@ -73,6 +75,7 @@ private const val TAG = "BiometricViewBinder"
object BiometricViewBinder {

    /** Binds a [BiometricPromptLayout] to a [PromptViewModel]. */
    @SuppressLint("ClickableViewAccessibility")
    @JvmStatic
    fun bind(
        view: BiometricPromptLayout,
@@ -300,21 +303,19 @@ object BiometricViewBinder {

                // reuse the icon as a confirm button
                launch {
                    viewModel.isConfirmButtonVisible
                    viewModel.isIconConfirmButton
                        .map { isPending ->
                            when {
                                isPending && iconController.actsAsConfirmButton ->
                                    View.OnClickListener { viewModel.confirmAuthenticated() }
                                else -> null
                                    View.OnTouchListener { _: View, event: MotionEvent ->
                                        viewModel.onOverlayTouch(event)
                                    }
                                else -> null
                            }
                        .collect { onClick ->
                            iconViewOverlay.setOnClickListener(onClick)
                            iconView.setOnClickListener(onClick)
                            if (onClick == null) {
                                iconViewOverlay.isClickable = false
                                iconView.isClickable = false
                        }
                        .collect { onTouch ->
                            iconViewOverlay.setOnTouchListener(onTouch)
                            iconView.setOnTouchListener(onTouch)
                        }
                }

@@ -340,6 +341,14 @@ object BiometricViewBinder {
                            backgroundView.setOnClickListener(null)
                            backgroundView.importantForAccessibility =
                                IMPORTANT_FOR_ACCESSIBILITY_NO

                            // Allow icon to be used as confirmation button with a11y enabled
                            if (accessibilityManager.isTouchExplorationEnabled) {
                                iconViewOverlay.setOnClickListener {
                                    viewModel.confirmAuthenticated()
                                }
                                iconView.setOnClickListener { viewModel.confirmAuthenticated() }
                            }
                        }
                        if (authState.isAuthenticatedAndConfirmed) {
                            view.announceForAccessibility(
+40 −12
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.biometrics.ui.viewmodel
import android.hardware.biometrics.BiometricPrompt
import android.util.Log
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import com.android.systemui.biometrics.AuthBiometricView
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.model.BiometricModalities
@@ -67,11 +68,18 @@ constructor(
    /** If the user has successfully authenticated and confirmed (when explicitly required). */
    val isAuthenticated: Flow<PromptAuthState> = _isAuthenticated.asStateFlow()

    private val _isOverlayTouched: MutableStateFlow<Boolean> = MutableStateFlow(false)

    /**
     * If the API caller or the user's personal preferences require explicit confirmation after
     * successful authentication.
     */
    val isConfirmationRequired: Flow<Boolean> = interactor.isConfirmationRequired
    val isConfirmationRequired: Flow<Boolean> =
        combine(_isOverlayTouched, interactor.isConfirmationRequired) {
            isOverlayTouched,
            isConfirmationRequired ->
            !isOverlayTouched && isConfirmationRequired
        }

    /** The kind of credential the user has. */
    val credentialKind: Flow<PromptKind> = interactor.credentialKind
@@ -150,6 +158,12 @@ constructor(
            }
            .distinctUntilChanged()

    /** If the icon can be used as a confirmation button. */
    val isIconConfirmButton: Flow<Boolean> =
        combine(size, interactor.isConfirmationRequired) { size, isConfirmationRequired ->
            size.isNotSmall && isConfirmationRequired
        }

    /** If the negative button should be shown. */
    val isNegativeButtonVisible: Flow<Boolean> =
        combine(
@@ -298,8 +312,10 @@ constructor(
            if (message.isNotBlank()) PromptMessage.Help(message) else PromptMessage.Empty
        _forceMediumSize.value = true
        _legacyState.value =
            if (alreadyAuthenticated) {
            if (alreadyAuthenticated && isConfirmationRequired.first()) {
                AuthBiometricView.STATE_PENDING_CONFIRMATION
            } else if (alreadyAuthenticated && !isConfirmationRequired.first()) {
                AuthBiometricView.STATE_AUTHENTICATED
            } else {
                AuthBiometricView.STATE_HELP
            }
@@ -397,18 +413,10 @@ constructor(
    }

    private suspend fun needsExplicitConfirmation(modality: BiometricModality): Boolean {
        val availableModalities = modalities.first()
        val confirmationRequired = isConfirmationRequired.first()

        if (availableModalities.hasFaceAndFingerprint) {
            // coex only needs confirmation when face is successful, unless it happens on the
            // first attempt (i.e. without failure) before fingerprint scanning starts
            val fingerprintStarted = fingerprintStartMode.first() != FingerprintStartMode.Pending
        // Only worry about confirmationRequired if face was used to unlock
        if (modality == BiometricModality.Face) {
                return fingerprintStarted || confirmationRequired
            }
        }
        if (availableModalities.hasFaceOnly) {
            return confirmationRequired
        }
        // fingerprint only never requires confirmation
@@ -438,6 +446,26 @@ constructor(
        messageJob = null
    }

    /**
     * Touch event occurred on the overlay
     *
     * Tracks whether a finger is currently down to set [_isOverlayTouched] to be used as user
     * confirmation
     */
    fun onOverlayTouch(event: MotionEvent): Boolean {
        if (event.actionMasked == MotionEvent.ACTION_DOWN) {
            _isOverlayTouched.value = true

            if (_isAuthenticated.value.needsUserConfirmation) {
                confirmAuthenticated()
            }
            return true
        } else if (event.actionMasked == MotionEvent.ACTION_UP) {
            _isOverlayTouched.value = false
        }
        return false
    }

    /**
     * Switch to the credential view.
     *
+6 −1
Original line number Diff line number Diff line
@@ -527,6 +527,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
        val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
        val size by collectLastValue(viewModel.size)
        val legacyState by collectLastValue(viewModel.legacyState)
        val confirmationRequired by collectLastValue(viewModel.isConfirmationRequired)

        if (testCase.isCoex && testCase.authenticatedByFingerprint) {
            viewModel.ensureFingerprintHasStarted(isDelayed = true)
@@ -535,7 +536,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
        viewModel.showHelp(helpMessage)

        assertThat(size).isEqualTo(PromptSize.MEDIUM)
        if (confirmationRequired == true) {
            assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION)
        } else {
            assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED)
        }
        assertThat(message).isEqualTo(PromptMessage.Help(helpMessage))
        assertThat(messageVisible).isTrue()
        assertThat(authenticating).isFalse()