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

Commit c4e39ae1 authored by Austin Delgado's avatar Austin Delgado
Browse files

Update Biometric Prompt for private space

- Allows Biometric Prompt to verify tied profile password for to unlock
  storage
- Adds Biometric Prompt use for within apps in Private Space

Bug: 365094949
Bug: 365932447
Test: atest BiometricServiceTest
Test: atest CredentialInteractorImplTest
Flag: android.hardware.biometrics.private_space_bp

Change-Id: I48d31f11e286beb21a06283c00d9db912aa00eab
parent 8192775f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -54,3 +54,10 @@ flag {
  description: "This flag is for API changes related to Identity Check"
  bug: "373424727"
}

flag {
  name: "private_space_bp"
  namespace: "biometrics_framework"
  description: "Feature flag for biometric prompt improvements in private space"
  bug: "365554098"
}
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
    <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />

    <!-- Used to read storage for all users -->
    <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
+83 −51
Original line number Diff line number Diff line
@@ -3,7 +3,9 @@ package com.android.systemui.biometrics.domain.interactor
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResourcesManager
import android.content.pm.UserInfo
import android.hardware.biometrics.Flags
import android.os.UserManager
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
@@ -33,6 +35,7 @@ import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit

private const val USER_ID = 22
private const val OWNER_ID = 10
private const val OPERATION_ID = 100L
private const val MAX_ATTEMPTS = 5

@@ -67,7 +70,7 @@ class CredentialInteractorImplTest : SysuiTestCase() {
                lockPatternUtils,
                userManager,
                devicePolicyManager,
                systemClock
                systemClock,
            )
    }

@@ -115,12 +118,35 @@ class CredentialInteractorImplTest : SysuiTestCase() {

    @Test fun pinCredentialWhenBadAndThrottled() = pinCredential(badCredential(timeout = 5_000))

    private fun pinCredential(result: VerifyCredentialResponse) = runTest {
    @EnableFlags(Flags.FLAG_PRIVATE_SPACE_BP)
    @Test
    fun pinCredentialTiedProfileWhenGood() = pinCredential(goodCredential(), OWNER_ID)

    @EnableFlags(Flags.FLAG_PRIVATE_SPACE_BP)
    @Test
    fun pinCredentialTiedProfileWhenBad() = pinCredential(badCredential(), OWNER_ID)

    @EnableFlags(Flags.FLAG_PRIVATE_SPACE_BP)
    @Test
    fun pinCredentialTiedProfileWhenBadAndThrottled() =
        pinCredential(badCredential(timeout = 5_000), OWNER_ID)

    private fun pinCredential(result: VerifyCredentialResponse, credentialOwner: Int = USER_ID) =
        runTest {
            val usedAttempts = 1
            whenever(lockPatternUtils.getCurrentFailedPasswordAttempts(eq(USER_ID)))
                .thenReturn(usedAttempts)
        whenever(lockPatternUtils.verifyCredential(any(), eq(USER_ID), anyInt())).thenReturn(result)
        whenever(lockPatternUtils.verifyGatekeeperPasswordHandle(anyLong(), anyLong(), eq(USER_ID)))
            whenever(lockPatternUtils.verifyCredential(any(), eq(USER_ID), anyInt()))
                .thenReturn(result)
            whenever(lockPatternUtils.verifyTiedProfileChallenge(any(), eq(USER_ID), anyInt()))
                .thenReturn(result)
            whenever(
                    lockPatternUtils.verifyGatekeeperPasswordHandle(
                        anyLong(),
                        anyLong(),
                        eq(USER_ID),
                    )
                )
                .thenReturn(result)
            whenever(lockPatternUtils.setLockoutAttemptDeadline(anyInt(), anyInt())).thenAnswer {
                systemClock.elapsedRealtime() + (it.arguments[1] as Int)
@@ -130,7 +156,10 @@ class CredentialInteractorImplTest : SysuiTestCase() {
            // checks prevents the method from returning
            val statusList = mutableListOf<CredentialStatus>()
            interactor
            .verifyCredential(pinRequest(), LockscreenCredential.createPin("1234"))
                .verifyCredential(
                    pinRequest(credentialOwner),
                    LockscreenCredential.createPin("1234"),
                )
                .toList(statusList)

            val last = statusList.removeLastOrNull()
@@ -154,10 +183,13 @@ class CredentialInteractorImplTest : SysuiTestCase() {
                    // messages are in the throttled errors, so the final Error.error is empty
                    assertThat(failedResult.error).isEmpty()
                    assertThat(statusList).isNotEmpty()
                assertThat(statusList.filterIsInstance(CredentialStatus.Fail.Throttled::class.java))
                    assertThat(
                            statusList.filterIsInstance(CredentialStatus.Fail.Throttled::class.java)
                        )
                        .hasSize(statusList.size)

                verify(lockPatternUtils).setLockoutAttemptDeadline(eq(USER_ID), eq(result.timeout))
                    verify(lockPatternUtils)
                        .setLockoutAttemptDeadline(eq(USER_ID), eq(result.timeout))
                } else { // failed
                    assertThat(failedResult.error)
                        .matches(Regex("(.*)try again(.*)", RegexOption.IGNORE_CASE).toPattern())
@@ -212,11 +244,11 @@ class CredentialInteractorImplTest : SysuiTestCase() {
    }
}

private fun pinRequest(): BiometricPromptRequest.Credential.Pin =
private fun pinRequest(credentialOwner: Int = USER_ID): BiometricPromptRequest.Credential.Pin =
    BiometricPromptRequest.Credential.Pin(
        promptInfo(),
        BiometricUserInfo(USER_ID),
        BiometricOperationInfo(OPERATION_ID)
        BiometricUserInfo(userId = USER_ID, deviceCredentialOwnerId = credentialOwner),
        BiometricOperationInfo(OPERATION_ID),
    )

private fun goodCredential(
+6 −4
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() {
    private val fingerprintRepository = FakeFingerprintPropertyRepository()
    private val promptRepository = FakePromptRepository()
    private val fakeExecutor = FakeExecutor(FakeSystemClock())
    private val credentialInteractor = FakeCredentialInteractor()

    private lateinit var displayStateRepository: FakeDisplayStateRepository
    private lateinit var displayRepository: FakeDisplayRepository
@@ -99,8 +100,9 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() {
            PromptSelectorInteractorImpl(
                fingerprintRepository,
                displayStateInteractor,
                credentialInteractor,
                promptRepository,
                lockPatternUtils
                lockPatternUtils,
            )
    }

@@ -134,13 +136,13 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() {
        testScope.runTest {
            useBiometricsAndReset(
                allowCredentialFallback = true,
                setComponentNameForConfirmDeviceCredentialActivity = true
                setComponentNameForConfirmDeviceCredentialActivity = true,
            )
        }

    private fun TestScope.useBiometricsAndReset(
        allowCredentialFallback: Boolean,
        setComponentNameForConfirmDeviceCredentialActivity: Boolean = false
        setComponentNameForConfirmDeviceCredentialActivity: Boolean = false,
    ) {
        setUserCredentialType(isPassword = true)

@@ -357,7 +359,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() {

    private fun setPrompt(
        info: PromptInfo = basicPromptInfo(),
        onSwitchToCredential: Boolean = false
        onSwitchToCredential: Boolean = false,
    ) {
        interactor.setPrompt(
            info,
+1 −1
Original line number Diff line number Diff line
@@ -323,7 +323,7 @@ public class AuthContainerView extends LinearLayout
        final boolean isLandscape = mContext.getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE;
        mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
        mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mEffectiveUserId,
        mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mConfig.mUserId,
                getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName,
                false /*onSwitchToCredential*/, isLandscape);

Loading