Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +4 −1 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import com.android.systemui.haptics.vibratorHelper import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.keyguard.wakefulnessLifecycle import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.testKosmos Loading Loading @@ -674,7 +675,9 @@ open class AuthContainerViewTest : SysuiTestCase() { kosmos.lockPatternUtils, kosmos.interactionJankMonitor, { kosmos.promptSelectorInteractor }, kosmos.promptViewModel, kosmos.promptViewModel.apply { this.iconViewModel.internal.activateIn(kosmos.testScope) }, { kosmos.credentialViewModel }, kosmos.fakeExecutor, kosmos.vibratorHelper, Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/BiometricAuthIconViewModelTest.kt 0 → 100644 +288 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.systemui.biometrics.ui.viewmodel import android.hardware.biometrics.PromptInfo import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.facePropertyRepository import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor 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.BiometricModalities import com.android.systemui.biometrics.shared.model.FaceSensorInfo import com.android.systemui.biometrics.shared.model.FingerprintSensorInfo import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.biometrics.shared.model.toSensorType import com.android.systemui.biometrics.ui.viewmodel.BiometricAuthIconViewModel.BiometricAuthModalities import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.securelockdevice.ui.viewmodel.SecureLockDeviceBiometricAuthContentViewModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class BiometricAuthIconViewModelTest() : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val defaultHelpMsg = "default help msg" private var promptViewModel: PromptViewModel? = null private var secureLockDeviceViewModel: SecureLockDeviceBiometricAuthContentViewModel? = null private var faceSensorInfo: FaceSensorInfo? = null private var fingerprintSensorInfo: FingerprintSensorInfo? = null private lateinit var underTest: BiometricAuthIconViewModel private fun enrollFingerprint( sensorStrength: SensorStrength = SensorStrength.STRONG, @FingerprintSensorProperties.SensorType sensorType: Int, ) { fingerprintSensorInfo = FingerprintSensorInfo(type = sensorType.toSensorType(), strength = sensorStrength) if (sensorType == TYPE_POWER_BUTTON) { kosmos.fingerprintPropertyRepository.supportsSideFps(sensorStrength) } else if (sensorType == TYPE_UDFPS_OPTICAL || sensorType == TYPE_UDFPS_ULTRASONIC) { kosmos.fingerprintPropertyRepository.supportsUdfps(sensorStrength) } else if (sensorType == TYPE_REAR) { kosmos.fingerprintPropertyRepository.supportsRearFps(sensorStrength) } } private fun enrollFace(isStrongBiometric: Boolean) { faceSensorInfo = FaceSensorInfo( id = 0, strength = if (isStrongBiometric) SensorStrength.STRONG else SensorStrength.WEAK, ) kosmos.facePropertyRepository.setSensorInfo(faceSensorInfo) } private fun startBiometricPrompt(hasFpAuth: Boolean, isImplicitFlow: Boolean = false) { if (isImplicitFlow) { promptViewModel!!.showAuthenticating() } else { promptViewModel!!.ensureFingerprintHasStarted(isDelayed = false) val helpMsg = if (hasFpAuth) { defaultHelpMsg } else { "" } promptViewModel!!.showAuthenticating(helpMsg) } } private fun initPromptViewModel() { promptViewModel = kosmos.promptViewModel underTest = promptViewModel!!.iconViewModel.internal underTest.activateIn(testScope) } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_face() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFace(isStrongBiometric = false) runCurrent() val faceProps = faceSensorPropertiesInternal().first() kosmos.promptSelectorInteractor.initializePrompt(null, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = false) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Face) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_sfps() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_POWER_BUTTON) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(sensorType = TYPE_POWER_BUTTON).first() val faceProps = faceSensorPropertiesInternal().first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Sfps) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_nonSfps() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_UDFPS_OPTICAL) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_UDFPS_OPTICAL) .first() val faceProps = faceSensorPropertiesInternal().first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.NonSfps) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_sfpsCoexImplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_POWER_BUTTON) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true, isImplicitFlow = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Face) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_coexSfpsExplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_POWER_BUTTON) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.SfpsCoex) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_coexNonSfpsImplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_UDFPS_OPTICAL) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true, isImplicitFlow = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Face) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_coexNonSfpsExplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_UDFPS_OPTICAL) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.NonSfpsCoex) } } /** Initialize the prompt according to the test configuration. */ private fun PromptSelectorInteractor.initializePrompt( fingerprint: FingerprintSensorPropertiesInternal? = null, face: FaceSensorPropertiesInternal? = null, requireConfirmation: Boolean = false, ) { val info = PromptInfo().apply { logoDescription = "logo" title = "title" subtitle = "subtitle" description = "description" contentView = null authenticators = listOf(face, fingerprint).extractAuthenticatorTypes() isDeviceCredentialAllowed = false isConfirmationRequested = requireConfirmation } setPrompt( info, 0, 0, BiometricModalities(fingerprintSensorInfo, faceSensorInfo), 0L, "packageName", onSwitchToCredential = false, isLandscape = false, ) } } packages/SystemUI/multivalentTests/src/com/android/systemui/securelockdevice/domain/interactor/SecureLockDeviceInteractorTest.kt +72 −1 Original line number Diff line number Diff line Loading @@ -16,18 +16,28 @@ package com.android.systemui.securelockdevice.domain.interactor import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON import android.platform.test.annotations.EnableFlags import android.security.Flags.FLAG_SECURE_LOCK_DEVICE import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fakeFacePropertyRepository import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.android.systemui.biometrics.shared.model.BiometricModalities import com.android.systemui.biometrics.shared.model.toFaceSensorInfo import com.android.systemui.biometrics.shared.model.toFingerprintSensorInfo import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.kosmos.testScope import com.android.systemui.securelockdevice.data.repository.fakeSecureLockDeviceRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat 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 Loading @@ -41,7 +51,14 @@ class SecureLockDeviceInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos() private val underTest: SecureLockDeviceInteractor = kosmos.secureLockDeviceInteractor private val testScope = kosmos.testScope private val underTest = kosmos.secureLockDeviceInteractor @Before fun setup() { kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) } @Test fun secureLockDeviceStateUpdates_acrossAuthenticationProgress() = Loading Loading @@ -74,4 +91,58 @@ class SecureLockDeviceInteractorTest : SysuiTestCase() { assertThat(requiresPrimaryAuthForSecureLockDevice).isEqualTo(false) assertThat(requiresStrongBiometricAuthForSecureLockDevice).isEqualTo(false) } @Test fun updatesModalitiesFromInteractor_strongFp() { testScope.runTest { val modalities by collectLastValue(underTest.enrolledStrongBiometricModalities) val fpSensorInfo = fingerprintSensorPropertiesInternal(sensorType = TYPE_POWER_BUTTON) .first() .toFingerprintSensorInfo() assertThat(modalities).isEqualTo(BiometricModalities()) kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) kosmos.fakeFingerprintPropertyRepository.supportsSideFps() runCurrent() assertThat(modalities).isEqualTo(BiometricModalities(fpSensorInfo, null)) } } @Test fun updatesModalitiesFromInteractor_strongFace() { testScope.runTest { val modalities by collectLastValue(underTest.enrolledStrongBiometricModalities) val faceSensorInfo = faceSensorPropertiesInternal().first().toFaceSensorInfo() assertThat(modalities).isEqualTo(BiometricModalities()) kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) kosmos.fakeFacePropertyRepository.setSensorInfo(faceSensorInfo) runCurrent() assertThat(modalities).isEqualTo(BiometricModalities(null, faceSensorInfo)) } } @Test fun updatesModalitiesFromInteractor_strongCoex() { testScope.runTest { val modalities by collectLastValue(underTest.enrolledStrongBiometricModalities) val fpSensorInfo = fingerprintSensorPropertiesInternal(sensorType = TYPE_POWER_BUTTON) .first() .toFingerprintSensorInfo() val faceSensorInfo = faceSensorPropertiesInternal().first().toFaceSensorInfo() assertThat(modalities).isEqualTo(BiometricModalities()) kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) kosmos.fakeFingerprintPropertyRepository.supportsSideFps() kosmos.fakeFacePropertyRepository.setSensorInfo(faceSensorInfo) runCurrent() assertThat(modalities).isEqualTo(BiometricModalities(fpSensorInfo, faceSensorInfo)) } } } packages/SystemUI/src/com/android/systemui/biometrics/BiometricAuthIconAssets.kt 0 → 100644 +168 −0 File added.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/src/com/android/systemui/biometrics/ui/PromptIconState.kt +2 −2 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.systemui.biometrics.ui import android.annotation.RawRes import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel import com.android.systemui.biometrics.ui.viewmodel.BiometricAuthIconViewModel /* Class for handling the state of the icon in Biometric prompt */ data class PromptIconState( Loading @@ -26,6 +26,6 @@ data class PromptIconState( val shouldLoop: Boolean, val contentDescriptionId: Int, val rotation: Float, val authType: PromptIconViewModel.AuthType, val activeBiometricAuthType: BiometricAuthIconViewModel.BiometricAuthModalities, val showingError: Boolean, ) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +4 −1 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import com.android.systemui.haptics.vibratorHelper import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.keyguard.wakefulnessLifecycle import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.testKosmos Loading Loading @@ -674,7 +675,9 @@ open class AuthContainerViewTest : SysuiTestCase() { kosmos.lockPatternUtils, kosmos.interactionJankMonitor, { kosmos.promptSelectorInteractor }, kosmos.promptViewModel, kosmos.promptViewModel.apply { this.iconViewModel.internal.activateIn(kosmos.testScope) }, { kosmos.credentialViewModel }, kosmos.fakeExecutor, kosmos.vibratorHelper, Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/BiometricAuthIconViewModelTest.kt 0 → 100644 +288 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.systemui.biometrics.ui.viewmodel import android.hardware.biometrics.PromptInfo import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.facePropertyRepository import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor 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.BiometricModalities import com.android.systemui.biometrics.shared.model.FaceSensorInfo import com.android.systemui.biometrics.shared.model.FingerprintSensorInfo import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.biometrics.shared.model.toSensorType import com.android.systemui.biometrics.ui.viewmodel.BiometricAuthIconViewModel.BiometricAuthModalities import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.securelockdevice.ui.viewmodel.SecureLockDeviceBiometricAuthContentViewModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class BiometricAuthIconViewModelTest() : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val defaultHelpMsg = "default help msg" private var promptViewModel: PromptViewModel? = null private var secureLockDeviceViewModel: SecureLockDeviceBiometricAuthContentViewModel? = null private var faceSensorInfo: FaceSensorInfo? = null private var fingerprintSensorInfo: FingerprintSensorInfo? = null private lateinit var underTest: BiometricAuthIconViewModel private fun enrollFingerprint( sensorStrength: SensorStrength = SensorStrength.STRONG, @FingerprintSensorProperties.SensorType sensorType: Int, ) { fingerprintSensorInfo = FingerprintSensorInfo(type = sensorType.toSensorType(), strength = sensorStrength) if (sensorType == TYPE_POWER_BUTTON) { kosmos.fingerprintPropertyRepository.supportsSideFps(sensorStrength) } else if (sensorType == TYPE_UDFPS_OPTICAL || sensorType == TYPE_UDFPS_ULTRASONIC) { kosmos.fingerprintPropertyRepository.supportsUdfps(sensorStrength) } else if (sensorType == TYPE_REAR) { kosmos.fingerprintPropertyRepository.supportsRearFps(sensorStrength) } } private fun enrollFace(isStrongBiometric: Boolean) { faceSensorInfo = FaceSensorInfo( id = 0, strength = if (isStrongBiometric) SensorStrength.STRONG else SensorStrength.WEAK, ) kosmos.facePropertyRepository.setSensorInfo(faceSensorInfo) } private fun startBiometricPrompt(hasFpAuth: Boolean, isImplicitFlow: Boolean = false) { if (isImplicitFlow) { promptViewModel!!.showAuthenticating() } else { promptViewModel!!.ensureFingerprintHasStarted(isDelayed = false) val helpMsg = if (hasFpAuth) { defaultHelpMsg } else { "" } promptViewModel!!.showAuthenticating(helpMsg) } } private fun initPromptViewModel() { promptViewModel = kosmos.promptViewModel underTest = promptViewModel!!.iconViewModel.internal underTest.activateIn(testScope) } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_face() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFace(isStrongBiometric = false) runCurrent() val faceProps = faceSensorPropertiesInternal().first() kosmos.promptSelectorInteractor.initializePrompt(null, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = false) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Face) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_sfps() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_POWER_BUTTON) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(sensorType = TYPE_POWER_BUTTON).first() val faceProps = faceSensorPropertiesInternal().first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Sfps) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_nonSfps() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_UDFPS_OPTICAL) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_UDFPS_OPTICAL) .first() val faceProps = faceSensorPropertiesInternal().first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.NonSfps) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_sfpsCoexImplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_POWER_BUTTON) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true, isImplicitFlow = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Face) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_coexSfpsExplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_POWER_BUTTON) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.SfpsCoex) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_coexNonSfpsImplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_UDFPS_OPTICAL) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true, isImplicitFlow = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.Face) } } @Test fun activeBiometricAuthType_basedOnModalitiesAndFaceMode_forBiometricPrompt_coexNonSfpsExplicit() { testScope.runTest { initPromptViewModel() val activeBiometricAuthType by collectLastValue(underTest.activeBiometricAuthType) enrollFingerprint(sensorType = TYPE_UDFPS_OPTICAL) enrollFace(isStrongBiometric = false) runCurrent() val fpProps = fingerprintSensorPropertiesInternal(strong = false, sensorType = TYPE_POWER_BUTTON) .first() val faceProps = faceSensorPropertiesInternal(strong = false).first() kosmos.promptSelectorInteractor.initializePrompt(fpProps, faceProps) runCurrent() startBiometricPrompt(hasFpAuth = true) assertThat(activeBiometricAuthType).isEqualTo(BiometricAuthModalities.NonSfpsCoex) } } /** Initialize the prompt according to the test configuration. */ private fun PromptSelectorInteractor.initializePrompt( fingerprint: FingerprintSensorPropertiesInternal? = null, face: FaceSensorPropertiesInternal? = null, requireConfirmation: Boolean = false, ) { val info = PromptInfo().apply { logoDescription = "logo" title = "title" subtitle = "subtitle" description = "description" contentView = null authenticators = listOf(face, fingerprint).extractAuthenticatorTypes() isDeviceCredentialAllowed = false isConfirmationRequested = requireConfirmation } setPrompt( info, 0, 0, BiometricModalities(fingerprintSensorInfo, faceSensorInfo), 0L, "packageName", onSwitchToCredential = false, isLandscape = false, ) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/securelockdevice/domain/interactor/SecureLockDeviceInteractorTest.kt +72 −1 Original line number Diff line number Diff line Loading @@ -16,18 +16,28 @@ package com.android.systemui.securelockdevice.domain.interactor import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON import android.platform.test.annotations.EnableFlags import android.security.Flags.FLAG_SECURE_LOCK_DEVICE import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fakeFacePropertyRepository import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.android.systemui.biometrics.shared.model.BiometricModalities import com.android.systemui.biometrics.shared.model.toFaceSensorInfo import com.android.systemui.biometrics.shared.model.toFingerprintSensorInfo import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.kosmos.testScope import com.android.systemui.securelockdevice.data.repository.fakeSecureLockDeviceRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat 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 Loading @@ -41,7 +51,14 @@ class SecureLockDeviceInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos() private val underTest: SecureLockDeviceInteractor = kosmos.secureLockDeviceInteractor private val testScope = kosmos.testScope private val underTest = kosmos.secureLockDeviceInteractor @Before fun setup() { kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) } @Test fun secureLockDeviceStateUpdates_acrossAuthenticationProgress() = Loading Loading @@ -74,4 +91,58 @@ class SecureLockDeviceInteractorTest : SysuiTestCase() { assertThat(requiresPrimaryAuthForSecureLockDevice).isEqualTo(false) assertThat(requiresStrongBiometricAuthForSecureLockDevice).isEqualTo(false) } @Test fun updatesModalitiesFromInteractor_strongFp() { testScope.runTest { val modalities by collectLastValue(underTest.enrolledStrongBiometricModalities) val fpSensorInfo = fingerprintSensorPropertiesInternal(sensorType = TYPE_POWER_BUTTON) .first() .toFingerprintSensorInfo() assertThat(modalities).isEqualTo(BiometricModalities()) kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) kosmos.fakeFingerprintPropertyRepository.supportsSideFps() runCurrent() assertThat(modalities).isEqualTo(BiometricModalities(fpSensorInfo, null)) } } @Test fun updatesModalitiesFromInteractor_strongFace() { testScope.runTest { val modalities by collectLastValue(underTest.enrolledStrongBiometricModalities) val faceSensorInfo = faceSensorPropertiesInternal().first().toFaceSensorInfo() assertThat(modalities).isEqualTo(BiometricModalities()) kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) kosmos.fakeFacePropertyRepository.setSensorInfo(faceSensorInfo) runCurrent() assertThat(modalities).isEqualTo(BiometricModalities(null, faceSensorInfo)) } } @Test fun updatesModalitiesFromInteractor_strongCoex() { testScope.runTest { val modalities by collectLastValue(underTest.enrolledStrongBiometricModalities) val fpSensorInfo = fingerprintSensorPropertiesInternal(sensorType = TYPE_POWER_BUTTON) .first() .toFingerprintSensorInfo() val faceSensorInfo = faceSensorPropertiesInternal().first().toFaceSensorInfo() assertThat(modalities).isEqualTo(BiometricModalities()) kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) kosmos.fakeFingerprintPropertyRepository.supportsSideFps() kosmos.fakeFacePropertyRepository.setSensorInfo(faceSensorInfo) runCurrent() assertThat(modalities).isEqualTo(BiometricModalities(fpSensorInfo, faceSensorInfo)) } } }
packages/SystemUI/src/com/android/systemui/biometrics/BiometricAuthIconAssets.kt 0 → 100644 +168 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/src/com/android/systemui/biometrics/ui/PromptIconState.kt +2 −2 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.systemui.biometrics.ui import android.annotation.RawRes import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel import com.android.systemui.biometrics.ui.viewmodel.BiometricAuthIconViewModel /* Class for handling the state of the icon in Biometric prompt */ data class PromptIconState( Loading @@ -26,6 +26,6 @@ data class PromptIconState( val shouldLoop: Boolean, val contentDescriptionId: Int, val rotation: Float, val authType: PromptIconViewModel.AuthType, val activeBiometricAuthType: BiometricAuthIconViewModel.BiometricAuthModalities, val showingError: Boolean, )