Support BP retry face auth on SFPS acquired
Bug: 328638260
Test: atest PromptViewModelTest
Flag: None
Change-Id: I9566aa5ecab6d96054baba1eebcfa2748834f9d3
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index e48f05d..66daddf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -457,6 +457,23 @@
}
}
}
+
+ // 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()
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 36c7b44..ec0e95a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -22,6 +22,7 @@
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 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.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 @@
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 @@
/** 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)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 10b86ea..13bc8dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -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 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 @@
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 @@
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 @@
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 @@
promptSelectorInteractor,
context,
udfpsOverlayInteractor,
+ biometricStatusInteractor,
udfpsUtils
),
{ credentialViewModel },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 2f3bab4..5f975ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -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.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.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.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 @@
@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 @@
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 @@
selectedUserInteractor,
testScope.backgroundScope
)
+ biometricStatusRepository = FakeBiometricStatusRepository()
+ biometricStatusInteractor =
+ BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
selector =
PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
selector.resetPrompt()
@@ -168,6 +181,7 @@
selector,
mContext,
udfpsOverlayInteractor,
+ biometricStatusInteractor,
udfpsUtils
)
iconViewModel = viewModel.iconViewModel
@@ -1043,8 +1057,7 @@
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,14 +1072,18 @@
assertThat(canTryAgain).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- if (testCase.isFaceOnly && expectConfirmation) {
- assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertButtonsVisible(
- cancel = true,
- confirm = true,
- )
+ if (expectConfirmation) {
+ if (testCase.isFaceOnly) {
+ assertThat(size).isEqualTo(PromptSize.MEDIUM)
+ assertButtonsVisible(
+ cancel = true,
+ confirm = true,
+ )
- viewModel.confirmAuthenticated()
+ viewModel.confirmAuthenticated()
+ } else if (testCase.isCoex) {
+ assertThat(authenticated?.isAuthenticatedAndConfirmed).isTrue()
+ }
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1076,8 +1093,7 @@
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 @@
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 {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 9cbe633..2ae6f542 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -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 @@
promptSelectorInteractor = promptSelectorInteractor,
context = applicationContext,
udfpsOverlayInteractor = udfpsOverlayInteractor,
+ biometricStatusInteractor = biometricStatusInteractor,
udfpsUtils = udfpsUtils
)
}