Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.widget.ScrollView import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN import com.android.internal.widget.lockPatternUtils import com.android.internal.widget.lockPatternUtils Loading Loading @@ -113,6 +114,8 @@ open class AuthContainerViewTest : SysuiTestCase() { whenever(packageManager.getPackageInfo(any(String::class.java), anyInt())) whenever(packageManager.getPackageInfo(any(String::class.java), anyInt())) .thenReturn(PackageInfo()) .thenReturn(PackageInfo()) context.setMockPackageManager(packageManager) context.setMockPackageManager(packageManager) whenever(lockPatternUtils.getCredentialTypeForUser(anyInt())) .thenReturn(CREDENTIAL_TYPE_PASSWORD) } } @After @After Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +34 −7 Original line number Original line Diff line number Diff line Loading @@ -16,16 +16,19 @@ package com.android.systemui.biometrics.domain.interactor package com.android.systemui.biometrics.domain.interactor import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.ComponentName import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.Flags import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptVerticalListContentView import android.hardware.biometrics.PromptVerticalListContentView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository Loading Loading @@ -172,6 +175,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { val isCredentialAllowed by collectLastValue(interactor.isCredentialAllowed) val isCredentialAllowed by collectLastValue(interactor.isCredentialAllowed) val credentialKind by collectLastValue(interactor.credentialKind) val credentialKind by collectLastValue(interactor.credentialKind) val isConfirmationRequired by collectLastValue(interactor.isConfirmationRequired) val isConfirmationRequired by collectLastValue(interactor.isConfirmationRequired) val currentView by collectLastValue(interactor.currentView) assertThat(currentPrompt).isNull() assertThat(currentPrompt).isNull() Loading @@ -192,6 +196,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { assertThat(currentPrompt?.subtitle).isEqualTo(SUBTITLE) assertThat(currentPrompt?.subtitle).isEqualTo(SUBTITLE) assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT) assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT) assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME) assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME) assertThat(currentView).isEqualTo(BiometricPromptView.BIOMETRIC) assertThat(promptKind!!.isBiometric()).isTrue() assertThat(promptKind!!.isBiometric()).isTrue() assertThat(currentPrompt?.componentNameForConfirmDeviceCredentialActivity) assertThat(currentPrompt?.componentNameForConfirmDeviceCredentialActivity) .isEqualTo( .isEqualTo( Loading @@ -203,8 +208,12 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { if (allowCredentialFallback) { if (allowCredentialFallback) { assertThat(credentialKind).isSameInstanceAs(PromptKind.Password) assertThat(credentialKind).isSameInstanceAs(PromptKind.Password) assertThat(isCredentialAllowed).isTrue() assertThat(isCredentialAllowed).isTrue() } else { if (Flags.bpFallbackOptions()) { assertThat(credentialKind).isEqualTo(PromptKind.Password) } else { } else { assertThat(credentialKind).isEqualTo(PromptKind.None) assertThat(credentialKind).isEqualTo(PromptKind.None) } assertThat(isCredentialAllowed).isFalse() assertThat(isCredentialAllowed).isFalse() } } assertThat(isConfirmationRequired).isEqualTo(confirmationRequired) assertThat(isConfirmationRequired).isEqualTo(confirmationRequired) Loading Loading @@ -282,11 +291,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { setUserCredentialType(isPassword = true) setUserCredentialType(isPassword = true) val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(onSwitchToCredential = true) setPrompt(onSwitchToCredential = true) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(currentView).isEqualTo(BiometricPromptView.CREDENTIAL) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -303,11 +314,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(info) setPrompt(info) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(currentView).isEqualTo(BiometricPromptView.CREDENTIAL) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -328,11 +341,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(info) setPrompt(info) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(currentView).isEqualTo(BiometricPromptView.CREDENTIAL) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -351,11 +366,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(info) setPrompt(info) assertThat(promptKind?.isOnePaneNoSensorLandscapeBiometric()).isTrue() assertThat(promptKind?.isOnePaneNoSensorLandscapeBiometric()).isTrue() assertThat(currentView).isEqualTo(BiometricPromptView.BIOMETRIC) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading Loading @@ -413,7 +430,17 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { // not using biometrics, should be null with no fallback option // not using biometrics, should be null with no fallback option assertThat(currentPrompt).isNull() assertThat(currentPrompt).isNull() if (Flags.bpFallbackOptions()) { if (kind == PromptKind.Password) { assertThat(credentialKind).isEqualTo(PromptKind.Password) } else if (kind == PromptKind.Pin) { assertThat(credentialKind).isEqualTo(PromptKind.Pin) } else { assertThat(credentialKind).isEqualTo(PromptKind.Pattern) } } else { assertThat(credentialKind).isEqualTo(PromptKind.None) assertThat(credentialKind).isEqualTo(PromptKind.None) } interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -434,12 +461,12 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } private fun setUserCredentialType(isPin: Boolean = false, isPassword: Boolean = false) { private fun setUserCredentialType(isPin: Boolean = false, isPassword: Boolean = false) { whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(any())) whenever(lockPatternUtils.getCredentialTypeForUser(any())) .thenReturn( .thenReturn( when { when { isPin -> DevicePolicyManager.PASSWORD_QUALITY_NUMERIC isPin -> CREDENTIAL_TYPE_PIN isPassword -> DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC isPassword -> CREDENTIAL_TYPE_PASSWORD else -> DevicePolicyManager.PASSWORD_QUALITY_SOMETHING else -> CREDENTIAL_TYPE_PATTERN } } ) ) } } Loading packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml +2 −2 Original line number Original line Diff line number Diff line Loading @@ -35,7 +35,7 @@ android:layout_height="match_parent"> app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toLeftOf="@+id/rightGuideline" app:layout_constraintRight_toLeftOf="@+id/rightGuideline" app:layout_constraintLeft_toLeftOf="@+id/leftGuideline" app:layout_constraintLeft_toLeftOf="@+id/leftGuideline" app:layout_constraintTop_toTopOf="@+id/topGuideline" /> app:layout_constraintTop_toTopOf="@+id/topBarrier" /> <androidx.compose.ui.platform.ComposeView <androidx.compose.ui.platform.ComposeView android:id="@+id/fallback_view" android:id="@+id/fallback_view" Loading @@ -45,7 +45,7 @@ android:layout_height="match_parent"> android:fillViewport="true" android:fillViewport="true" app:layout_constrainedHeight="true" app:layout_constrainedHeight="true" app:layout_constrainedWidth="true" app:layout_constrainedWidth="true" app:layout_constraintBottom_toTopOf="@id/button_bar" app:layout_constraintBottom_toTopOf="@id/bottomGuideline" app:layout_constraintEnd_toEndOf="@id/panel" app:layout_constraintEnd_toEndOf="@id/panel" app:layout_constraintStart_toStartOf="@id/panel" app:layout_constraintStart_toStartOf="@id/panel" app:layout_constraintTop_toTopOf="@+id/topGuideline" app:layout_constraintTop_toTopOf="@+id/topGuideline" Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +8 −1 Original line number Original line Diff line number Diff line Loading @@ -350,9 +350,10 @@ public class AuthContainerView extends LinearLayout final boolean isLandscape = mContext.getResources().getConfiguration().orientation final boolean isLandscape = mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; == Configuration.ORIENTATION_LANDSCAPE; mPromptSelectorInteractorProvider = promptSelectorInteractorProvider; mPromptSelectorInteractorProvider = promptSelectorInteractorProvider; // If the intro (animation) is being skipped, don't reset the prompt mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mConfig.mUserId, mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mConfig.mUserId, getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName, getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName, false /*onSwitchToCredential*/, isLandscape); false /*onSwitchToCredential*/, isLandscape, !mConfig.mSkipIntro); final LayoutInflater layoutInflater = LayoutInflater.from(mContext); final LayoutInflater layoutInflater = LayoutInflater.from(mContext); final PromptKind kind = mPromptViewModel.getPromptKind().getValue(); final PromptKind kind = mPromptViewModel.getPromptKind().getValue(); Loading @@ -364,10 +365,16 @@ public class AuthContainerView extends LinearLayout mLayout = (ConstraintLayout) layoutInflater.inflate( mLayout = (ConstraintLayout) layoutInflater.inflate( R.layout.biometric_prompt_one_pane_layout, this, false /* attachToRoot */); R.layout.biometric_prompt_one_pane_layout, this, false /* attachToRoot */); } } // Setting visibility here to avoid unflagged layout changes if (Flags.bpFallbackOptions()) { mLayout.findViewById(R.id.auth_screen).setVisibility(View.GONE); } } else { } else { mLayout = (FrameLayout) layoutInflater.inflate( mLayout = (FrameLayout) layoutInflater.inflate( R.layout.auth_container_view, this, false /* attachToRoot */); R.layout.auth_container_view, this, false /* attachToRoot */); } } addView(mLayout); addView(mLayout); mBackgroundView = mLayout.findViewById(R.id.background); mBackgroundView = mLayout.findViewById(R.id.background); Loading packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt +37 −11 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,7 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.domain.interactor.DisplayStateInteractor import com.android.systemui.display.domain.interactor.DisplayStateInteractor import com.android.systemui.display.shared.model.isDefaultOrientation import com.android.systemui.display.shared.model.isDefaultOrientation import com.android.systemui.kairos.awaitClose import com.android.systemui.kairos.awaitClose import com.android.systemui.util.kotlin.combine import javax.inject.Inject import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow Loading Loading @@ -72,6 +73,9 @@ interface PromptSelectorInteractor { /** The kind of prompt to use (biometric, pin, pattern, etc.). */ /** The kind of prompt to use (biometric, pin, pattern, etc.). */ val promptKind: StateFlow<PromptKind> val promptKind: StateFlow<PromptKind> /** The modalities available in the prompt */ val modalities: Flow<BiometricModalities> /** If using a credential is allowed. */ /** If using a credential is allowed. */ val isCredentialAllowed: Flow<Boolean> val isCredentialAllowed: Flow<Boolean> Loading Loading @@ -125,6 +129,7 @@ interface PromptSelectorInteractor { opPackageName: String, opPackageName: String, onSwitchToCredential: Boolean, onSwitchToCredential: Boolean, isLandscape: Boolean, isLandscape: Boolean, updateView: Boolean = true, ) ) /** Unset the current authentication request. */ /** Unset the current authentication request. */ Loading @@ -151,7 +156,8 @@ constructor( promptRepository.userId, promptRepository.userId, promptRepository.promptKind, promptRepository.promptKind, promptRepository.opPackageName, promptRepository.opPackageName, ) { promptInfo, challenge, userId, kind, opPackageName -> promptRepository.modalities, ) { promptInfo, challenge, userId, kind, opPackageName, modalities -> if ( if ( promptInfo == null || userId == null || challenge == null || opPackageName == null promptInfo == null || userId == null || challenge == null || opPackageName == null ) { ) { Loading @@ -171,7 +177,7 @@ constructor( operationInfo = BiometricOperationInfo(gatekeeperChallenge = challenge), operationInfo = BiometricOperationInfo(gatekeeperChallenge = challenge), modalities = modalities = if (Flags.bpFallbackOptions()) { if (Flags.bpFallbackOptions()) { promptRepository.modalities.value modalities } else { } else { kind.activeModalities kind.activeModalities }, }, Loading @@ -183,6 +189,8 @@ constructor( override val promptKind: StateFlow<PromptKind> = promptRepository.promptKind override val promptKind: StateFlow<PromptKind> = promptRepository.promptKind override val modalities: StateFlow<BiometricModalities> = promptRepository.modalities override val isConfirmationRequired: Flow<Boolean> = override val isConfirmationRequired: Flow<Boolean> = promptRepository.isConfirmationRequired.distinctUntilChanged() promptRepository.isConfirmationRequired.distinctUntilChanged() Loading Loading @@ -236,6 +244,15 @@ constructor( override val fallbackOptions: Flow<List<FallbackOptionModel>> = promptRepository.fallbackOptions override val fallbackOptions: Flow<List<FallbackOptionModel>> = promptRepository.fallbackOptions override val credentialKind: Flow<PromptKind> = override val credentialKind: Flow<PromptKind> = if (Flags.bpFallbackOptions()) { promptRepository.userId.map { userId -> if (userId != null) { getCredentialType(lockPatternUtils, userId) } else { PromptKind.None } } } else { combine(prompt, isCredentialAllowed) { prompt, isAllowed -> combine(prompt, isCredentialAllowed) { prompt, isAllowed -> if (prompt != null && isAllowed) { if (prompt != null && isAllowed) { getCredentialType(lockPatternUtils, prompt.userInfo.deviceCredentialOwnerId) getCredentialType(lockPatternUtils, prompt.userInfo.deviceCredentialOwnerId) Loading @@ -243,6 +260,7 @@ constructor( PromptKind.None PromptKind.None } } } } } override val fingerprintSensorType: Flow<FingerprintSensorType> = override val fingerprintSensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType fingerprintPropertyRepository.sensorType Loading Loading @@ -298,6 +316,7 @@ constructor( opPackageName: String, opPackageName: String, onSwitchToCredential: Boolean, onSwitchToCredential: Boolean, isLandscape: Boolean, isLandscape: Boolean, updateView: Boolean, ) { ) { val effectiveUserId = credentialInteractor.getCredentialOwnerOrSelfId(userId) val effectiveUserId = credentialInteractor.getCredentialOwnerOrSelfId(userId) val hasCredentialViewShown = promptKind.value.isCredential() val hasCredentialViewShown = promptKind.value.isCredential() Loading @@ -309,11 +328,15 @@ constructor( val showBpWithoutIconForCredential = showBpForCredential && !hasCredentialViewShown val showBpWithoutIconForCredential = showBpForCredential && !hasCredentialViewShown var kind: PromptKind = PromptKind.None var kind: PromptKind = PromptKind.None if (onSwitchToCredential) { if (onSwitchToCredential || _currentView.value == BiometricPromptView.CREDENTIAL) { kind = getCredentialType(lockPatternUtils, effectiveUserId) kind = getCredentialType(lockPatternUtils, effectiveUserId) if (updateView) { _currentView.value = BiometricPromptView.CREDENTIAL _currentView.value = BiometricPromptView.CREDENTIAL } } else if (Utils.isBiometricAllowed(promptInfo) || showBpWithoutIconForCredential) { } else if (Utils.isBiometricAllowed(promptInfo) || showBpWithoutIconForCredential) { if (updateView) { _currentView.value = BiometricPromptView.BIOMETRIC _currentView.value = BiometricPromptView.BIOMETRIC } // TODO(b/330908557): Subscribe to // TODO(b/330908557): Subscribe to // displayStateInteractor.currentRotation.value.isDefaultOrientation() for checking // displayStateInteractor.currentRotation.value.isDefaultOrientation() for checking // `isLandscape` after removing AuthContainerView. // `isLandscape` after removing AuthContainerView. Loading @@ -332,7 +355,9 @@ constructor( PromptKind.Biometric(modalities) PromptKind.Biometric(modalities) } } } else if (isDeviceCredentialAllowed(promptInfo)) { } else if (isDeviceCredentialAllowed(promptInfo)) { if (updateView) { _currentView.value = BiometricPromptView.CREDENTIAL _currentView.value = BiometricPromptView.CREDENTIAL } kind = getCredentialType(lockPatternUtils, effectiveUserId) kind = getCredentialType(lockPatternUtils, effectiveUserId) } } Loading @@ -348,6 +373,7 @@ constructor( } } override fun resetPrompt(requestId: Long) { override fun resetPrompt(requestId: Long) { _currentView.value = BiometricPromptView.BIOMETRIC promptRepository.unsetPrompt(requestId) promptRepository.unsetPrompt(requestId) } } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.widget.ScrollView import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN import com.android.internal.widget.lockPatternUtils import com.android.internal.widget.lockPatternUtils Loading Loading @@ -113,6 +114,8 @@ open class AuthContainerViewTest : SysuiTestCase() { whenever(packageManager.getPackageInfo(any(String::class.java), anyInt())) whenever(packageManager.getPackageInfo(any(String::class.java), anyInt())) .thenReturn(PackageInfo()) .thenReturn(PackageInfo()) context.setMockPackageManager(packageManager) context.setMockPackageManager(packageManager) whenever(lockPatternUtils.getCredentialTypeForUser(anyInt())) .thenReturn(CREDENTIAL_TYPE_PASSWORD) } } @After @After Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +34 −7 Original line number Original line Diff line number Diff line Loading @@ -16,16 +16,19 @@ package com.android.systemui.biometrics.domain.interactor package com.android.systemui.biometrics.domain.interactor import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.ComponentName import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.Flags import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptVerticalListContentView import android.hardware.biometrics.PromptVerticalListContentView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository Loading Loading @@ -172,6 +175,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { val isCredentialAllowed by collectLastValue(interactor.isCredentialAllowed) val isCredentialAllowed by collectLastValue(interactor.isCredentialAllowed) val credentialKind by collectLastValue(interactor.credentialKind) val credentialKind by collectLastValue(interactor.credentialKind) val isConfirmationRequired by collectLastValue(interactor.isConfirmationRequired) val isConfirmationRequired by collectLastValue(interactor.isConfirmationRequired) val currentView by collectLastValue(interactor.currentView) assertThat(currentPrompt).isNull() assertThat(currentPrompt).isNull() Loading @@ -192,6 +196,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { assertThat(currentPrompt?.subtitle).isEqualTo(SUBTITLE) assertThat(currentPrompt?.subtitle).isEqualTo(SUBTITLE) assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT) assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT) assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME) assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME) assertThat(currentView).isEqualTo(BiometricPromptView.BIOMETRIC) assertThat(promptKind!!.isBiometric()).isTrue() assertThat(promptKind!!.isBiometric()).isTrue() assertThat(currentPrompt?.componentNameForConfirmDeviceCredentialActivity) assertThat(currentPrompt?.componentNameForConfirmDeviceCredentialActivity) .isEqualTo( .isEqualTo( Loading @@ -203,8 +208,12 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { if (allowCredentialFallback) { if (allowCredentialFallback) { assertThat(credentialKind).isSameInstanceAs(PromptKind.Password) assertThat(credentialKind).isSameInstanceAs(PromptKind.Password) assertThat(isCredentialAllowed).isTrue() assertThat(isCredentialAllowed).isTrue() } else { if (Flags.bpFallbackOptions()) { assertThat(credentialKind).isEqualTo(PromptKind.Password) } else { } else { assertThat(credentialKind).isEqualTo(PromptKind.None) assertThat(credentialKind).isEqualTo(PromptKind.None) } assertThat(isCredentialAllowed).isFalse() assertThat(isCredentialAllowed).isFalse() } } assertThat(isConfirmationRequired).isEqualTo(confirmationRequired) assertThat(isConfirmationRequired).isEqualTo(confirmationRequired) Loading Loading @@ -282,11 +291,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { setUserCredentialType(isPassword = true) setUserCredentialType(isPassword = true) val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(onSwitchToCredential = true) setPrompt(onSwitchToCredential = true) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(currentView).isEqualTo(BiometricPromptView.CREDENTIAL) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -303,11 +314,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(info) setPrompt(info) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(currentView).isEqualTo(BiometricPromptView.CREDENTIAL) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -328,11 +341,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(info) setPrompt(info) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(promptKind).isEqualTo(PromptKind.Password) assertThat(currentView).isEqualTo(BiometricPromptView.CREDENTIAL) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -351,11 +366,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } val promptKind by collectLastValue(interactor.promptKind) val promptKind by collectLastValue(interactor.promptKind) val currentView by collectLastValue(interactor.currentView) assertThat(promptKind).isEqualTo(PromptKind.None) assertThat(promptKind).isEqualTo(PromptKind.None) setPrompt(info) setPrompt(info) assertThat(promptKind?.isOnePaneNoSensorLandscapeBiometric()).isTrue() assertThat(promptKind?.isOnePaneNoSensorLandscapeBiometric()).isTrue() assertThat(currentView).isEqualTo(BiometricPromptView.BIOMETRIC) interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading Loading @@ -413,7 +430,17 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { // not using biometrics, should be null with no fallback option // not using biometrics, should be null with no fallback option assertThat(currentPrompt).isNull() assertThat(currentPrompt).isNull() if (Flags.bpFallbackOptions()) { if (kind == PromptKind.Password) { assertThat(credentialKind).isEqualTo(PromptKind.Password) } else if (kind == PromptKind.Pin) { assertThat(credentialKind).isEqualTo(PromptKind.Pin) } else { assertThat(credentialKind).isEqualTo(PromptKind.Pattern) } } else { assertThat(credentialKind).isEqualTo(PromptKind.None) assertThat(credentialKind).isEqualTo(PromptKind.None) } interactor.resetPrompt(REQUEST_ID) interactor.resetPrompt(REQUEST_ID) verifyUnset() verifyUnset() Loading @@ -434,12 +461,12 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { } } private fun setUserCredentialType(isPin: Boolean = false, isPassword: Boolean = false) { private fun setUserCredentialType(isPin: Boolean = false, isPassword: Boolean = false) { whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(any())) whenever(lockPatternUtils.getCredentialTypeForUser(any())) .thenReturn( .thenReturn( when { when { isPin -> DevicePolicyManager.PASSWORD_QUALITY_NUMERIC isPin -> CREDENTIAL_TYPE_PIN isPassword -> DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC isPassword -> CREDENTIAL_TYPE_PASSWORD else -> DevicePolicyManager.PASSWORD_QUALITY_SOMETHING else -> CREDENTIAL_TYPE_PATTERN } } ) ) } } Loading
packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml +2 −2 Original line number Original line Diff line number Diff line Loading @@ -35,7 +35,7 @@ android:layout_height="match_parent"> app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toLeftOf="@+id/rightGuideline" app:layout_constraintRight_toLeftOf="@+id/rightGuideline" app:layout_constraintLeft_toLeftOf="@+id/leftGuideline" app:layout_constraintLeft_toLeftOf="@+id/leftGuideline" app:layout_constraintTop_toTopOf="@+id/topGuideline" /> app:layout_constraintTop_toTopOf="@+id/topBarrier" /> <androidx.compose.ui.platform.ComposeView <androidx.compose.ui.platform.ComposeView android:id="@+id/fallback_view" android:id="@+id/fallback_view" Loading @@ -45,7 +45,7 @@ android:layout_height="match_parent"> android:fillViewport="true" android:fillViewport="true" app:layout_constrainedHeight="true" app:layout_constrainedHeight="true" app:layout_constrainedWidth="true" app:layout_constrainedWidth="true" app:layout_constraintBottom_toTopOf="@id/button_bar" app:layout_constraintBottom_toTopOf="@id/bottomGuideline" app:layout_constraintEnd_toEndOf="@id/panel" app:layout_constraintEnd_toEndOf="@id/panel" app:layout_constraintStart_toStartOf="@id/panel" app:layout_constraintStart_toStartOf="@id/panel" app:layout_constraintTop_toTopOf="@+id/topGuideline" app:layout_constraintTop_toTopOf="@+id/topGuideline" Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +8 −1 Original line number Original line Diff line number Diff line Loading @@ -350,9 +350,10 @@ public class AuthContainerView extends LinearLayout final boolean isLandscape = mContext.getResources().getConfiguration().orientation final boolean isLandscape = mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; == Configuration.ORIENTATION_LANDSCAPE; mPromptSelectorInteractorProvider = promptSelectorInteractorProvider; mPromptSelectorInteractorProvider = promptSelectorInteractorProvider; // If the intro (animation) is being skipped, don't reset the prompt mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mConfig.mUserId, mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mConfig.mUserId, getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName, getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName, false /*onSwitchToCredential*/, isLandscape); false /*onSwitchToCredential*/, isLandscape, !mConfig.mSkipIntro); final LayoutInflater layoutInflater = LayoutInflater.from(mContext); final LayoutInflater layoutInflater = LayoutInflater.from(mContext); final PromptKind kind = mPromptViewModel.getPromptKind().getValue(); final PromptKind kind = mPromptViewModel.getPromptKind().getValue(); Loading @@ -364,10 +365,16 @@ public class AuthContainerView extends LinearLayout mLayout = (ConstraintLayout) layoutInflater.inflate( mLayout = (ConstraintLayout) layoutInflater.inflate( R.layout.biometric_prompt_one_pane_layout, this, false /* attachToRoot */); R.layout.biometric_prompt_one_pane_layout, this, false /* attachToRoot */); } } // Setting visibility here to avoid unflagged layout changes if (Flags.bpFallbackOptions()) { mLayout.findViewById(R.id.auth_screen).setVisibility(View.GONE); } } else { } else { mLayout = (FrameLayout) layoutInflater.inflate( mLayout = (FrameLayout) layoutInflater.inflate( R.layout.auth_container_view, this, false /* attachToRoot */); R.layout.auth_container_view, this, false /* attachToRoot */); } } addView(mLayout); addView(mLayout); mBackgroundView = mLayout.findViewById(R.id.background); mBackgroundView = mLayout.findViewById(R.id.background); Loading
packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt +37 −11 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,7 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.domain.interactor.DisplayStateInteractor import com.android.systemui.display.domain.interactor.DisplayStateInteractor import com.android.systemui.display.shared.model.isDefaultOrientation import com.android.systemui.display.shared.model.isDefaultOrientation import com.android.systemui.kairos.awaitClose import com.android.systemui.kairos.awaitClose import com.android.systemui.util.kotlin.combine import javax.inject.Inject import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow Loading Loading @@ -72,6 +73,9 @@ interface PromptSelectorInteractor { /** The kind of prompt to use (biometric, pin, pattern, etc.). */ /** The kind of prompt to use (biometric, pin, pattern, etc.). */ val promptKind: StateFlow<PromptKind> val promptKind: StateFlow<PromptKind> /** The modalities available in the prompt */ val modalities: Flow<BiometricModalities> /** If using a credential is allowed. */ /** If using a credential is allowed. */ val isCredentialAllowed: Flow<Boolean> val isCredentialAllowed: Flow<Boolean> Loading Loading @@ -125,6 +129,7 @@ interface PromptSelectorInteractor { opPackageName: String, opPackageName: String, onSwitchToCredential: Boolean, onSwitchToCredential: Boolean, isLandscape: Boolean, isLandscape: Boolean, updateView: Boolean = true, ) ) /** Unset the current authentication request. */ /** Unset the current authentication request. */ Loading @@ -151,7 +156,8 @@ constructor( promptRepository.userId, promptRepository.userId, promptRepository.promptKind, promptRepository.promptKind, promptRepository.opPackageName, promptRepository.opPackageName, ) { promptInfo, challenge, userId, kind, opPackageName -> promptRepository.modalities, ) { promptInfo, challenge, userId, kind, opPackageName, modalities -> if ( if ( promptInfo == null || userId == null || challenge == null || opPackageName == null promptInfo == null || userId == null || challenge == null || opPackageName == null ) { ) { Loading @@ -171,7 +177,7 @@ constructor( operationInfo = BiometricOperationInfo(gatekeeperChallenge = challenge), operationInfo = BiometricOperationInfo(gatekeeperChallenge = challenge), modalities = modalities = if (Flags.bpFallbackOptions()) { if (Flags.bpFallbackOptions()) { promptRepository.modalities.value modalities } else { } else { kind.activeModalities kind.activeModalities }, }, Loading @@ -183,6 +189,8 @@ constructor( override val promptKind: StateFlow<PromptKind> = promptRepository.promptKind override val promptKind: StateFlow<PromptKind> = promptRepository.promptKind override val modalities: StateFlow<BiometricModalities> = promptRepository.modalities override val isConfirmationRequired: Flow<Boolean> = override val isConfirmationRequired: Flow<Boolean> = promptRepository.isConfirmationRequired.distinctUntilChanged() promptRepository.isConfirmationRequired.distinctUntilChanged() Loading Loading @@ -236,6 +244,15 @@ constructor( override val fallbackOptions: Flow<List<FallbackOptionModel>> = promptRepository.fallbackOptions override val fallbackOptions: Flow<List<FallbackOptionModel>> = promptRepository.fallbackOptions override val credentialKind: Flow<PromptKind> = override val credentialKind: Flow<PromptKind> = if (Flags.bpFallbackOptions()) { promptRepository.userId.map { userId -> if (userId != null) { getCredentialType(lockPatternUtils, userId) } else { PromptKind.None } } } else { combine(prompt, isCredentialAllowed) { prompt, isAllowed -> combine(prompt, isCredentialAllowed) { prompt, isAllowed -> if (prompt != null && isAllowed) { if (prompt != null && isAllowed) { getCredentialType(lockPatternUtils, prompt.userInfo.deviceCredentialOwnerId) getCredentialType(lockPatternUtils, prompt.userInfo.deviceCredentialOwnerId) Loading @@ -243,6 +260,7 @@ constructor( PromptKind.None PromptKind.None } } } } } override val fingerprintSensorType: Flow<FingerprintSensorType> = override val fingerprintSensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType fingerprintPropertyRepository.sensorType Loading Loading @@ -298,6 +316,7 @@ constructor( opPackageName: String, opPackageName: String, onSwitchToCredential: Boolean, onSwitchToCredential: Boolean, isLandscape: Boolean, isLandscape: Boolean, updateView: Boolean, ) { ) { val effectiveUserId = credentialInteractor.getCredentialOwnerOrSelfId(userId) val effectiveUserId = credentialInteractor.getCredentialOwnerOrSelfId(userId) val hasCredentialViewShown = promptKind.value.isCredential() val hasCredentialViewShown = promptKind.value.isCredential() Loading @@ -309,11 +328,15 @@ constructor( val showBpWithoutIconForCredential = showBpForCredential && !hasCredentialViewShown val showBpWithoutIconForCredential = showBpForCredential && !hasCredentialViewShown var kind: PromptKind = PromptKind.None var kind: PromptKind = PromptKind.None if (onSwitchToCredential) { if (onSwitchToCredential || _currentView.value == BiometricPromptView.CREDENTIAL) { kind = getCredentialType(lockPatternUtils, effectiveUserId) kind = getCredentialType(lockPatternUtils, effectiveUserId) if (updateView) { _currentView.value = BiometricPromptView.CREDENTIAL _currentView.value = BiometricPromptView.CREDENTIAL } } else if (Utils.isBiometricAllowed(promptInfo) || showBpWithoutIconForCredential) { } else if (Utils.isBiometricAllowed(promptInfo) || showBpWithoutIconForCredential) { if (updateView) { _currentView.value = BiometricPromptView.BIOMETRIC _currentView.value = BiometricPromptView.BIOMETRIC } // TODO(b/330908557): Subscribe to // TODO(b/330908557): Subscribe to // displayStateInteractor.currentRotation.value.isDefaultOrientation() for checking // displayStateInteractor.currentRotation.value.isDefaultOrientation() for checking // `isLandscape` after removing AuthContainerView. // `isLandscape` after removing AuthContainerView. Loading @@ -332,7 +355,9 @@ constructor( PromptKind.Biometric(modalities) PromptKind.Biometric(modalities) } } } else if (isDeviceCredentialAllowed(promptInfo)) { } else if (isDeviceCredentialAllowed(promptInfo)) { if (updateView) { _currentView.value = BiometricPromptView.CREDENTIAL _currentView.value = BiometricPromptView.CREDENTIAL } kind = getCredentialType(lockPatternUtils, effectiveUserId) kind = getCredentialType(lockPatternUtils, effectiveUserId) } } Loading @@ -348,6 +373,7 @@ constructor( } } override fun resetPrompt(requestId: Long) { override fun resetPrompt(requestId: Long) { _currentView.value = BiometricPromptView.BIOMETRIC promptRepository.unsetPrompt(requestId) promptRepository.unsetPrompt(requestId) } } Loading