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

Commit e6462e40 authored by Chandru S's avatar Chandru S Committed by Cherrypicker Worker
Browse files

Add FP running state and FP sensor type information to DeviceEntryFPAuthRepository

Bug: 262838215
Test: atest DeviceEntryFingerprintAuthRepositoryTest
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:86b1c272439ff87eb409d9dce1799a1d2ac6aba1)
Merged-In: I0e7196db2eda7a42885658dee22a7ae585b20af2
Change-Id: I0e7196db2eda7a42885658dee22a7ae585b20af2
parent 5308553f
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -2476,8 +2476,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
     * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
     */
    public boolean isUdfpsSupported() {
        return mAuthController.getUdfpsProps() != null
                && !mAuthController.getUdfpsProps().isEmpty();
        return mAuthController.isUdfpsSupported();
    }

    /**
@@ -2492,8 +2491,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
     * not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
     */
    public boolean isSfpsSupported() {
        return mAuthController.getSfpsProps() != null
                && !mAuthController.getSfpsProps().isEmpty();
        return mAuthController.isSfpsSupported();
    }

    /**
+30 −0
Original line number Diff line number Diff line
@@ -984,6 +984,36 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
        return mSidefpsProps;
    }

    /**
     * @return true if udfps HW is supported on this device. Can return true even if the user has
     * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
     */
    public boolean isUdfpsSupported() {
        return getUdfpsProps() != null && !getUdfpsProps().isEmpty();
    }

    /**
     * @return true if sfps HW is supported on this device. Can return true even if the user has
     * not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
     */
    public boolean isSfpsSupported() {
        return getSfpsProps() != null && !getSfpsProps().isEmpty();
    }

    /**
     * @return true if rear fps HW is supported on this device. Can return true even if the user has
     * not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
     */
    public boolean isRearFpsSupported() {
        for (FingerprintSensorPropertiesInternal prop: mFpProps) {
            if (prop.sensorType == TYPE_REAR) {
                return true;
            }
        }
        return false;
    }


    private String getErrorString(@Modality int modality, int error, int vendorCode) {
        switch (modality) {
            case TYPE_FACE:
+46 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -29,6 +30,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
@@ -37,6 +39,17 @@ import kotlinx.coroutines.flow.stateIn
interface DeviceEntryFingerprintAuthRepository {
    /** Whether the device entry fingerprint auth is locked out. */
    val isLockedOut: StateFlow<Boolean>

    /**
     * Whether the fingerprint sensor is currently listening, this doesn't mean that the user is
     * actively authenticating.
     */
    val isRunning: Flow<Boolean>

    /**
     * Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
     */
    val availableFpSensorType: BiometricType?
}

/**
@@ -50,6 +63,7 @@ interface DeviceEntryFingerprintAuthRepository {
class DeviceEntryFingerprintAuthRepositoryImpl
@Inject
constructor(
    val authController: AuthController,
    val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    @Application scope: CoroutineScope,
    dumpManager: DumpManager,
@@ -63,6 +77,12 @@ constructor(
        pw.println("isLockedOut=${isLockedOut.value}")
    }

    override val availableFpSensorType: BiometricType?
        get() =
            if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
            else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
            else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null

    override val isLockedOut: StateFlow<Boolean> =
        conflatedCallbackFlow {
                val sendLockoutUpdate =
@@ -89,6 +109,32 @@ constructor(
            }
            .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)

    override val isRunning: Flow<Boolean>
        get() = conflatedCallbackFlow {
            val callback =
                object : KeyguardUpdateMonitorCallback() {
                    override fun onBiometricRunningStateChanged(
                        running: Boolean,
                        biometricSourceType: BiometricSourceType?
                    ) {
                        if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
                            trySendWithFailureLogging(
                                running,
                                TAG,
                                "Fingerprint running state changed"
                            )
                        }
                    }
                }
            keyguardUpdateMonitor.registerCallback(callback)
            trySendWithFailureLogging(
                keyguardUpdateMonitor.isFingerprintDetectionRunning,
                TAG,
                "Initial fingerprint running state"
            )
            awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
        }

    companion object {
        const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
    }
+3 −8
Original line number Diff line number Diff line
@@ -1360,9 +1360,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled()
            throws RemoteException {
        // SFPS supported and enrolled
        final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
        props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
        when(mAuthController.getSfpsProps()).thenReturn(props);
        when(mAuthController.isSfpsSupported()).thenReturn(true);
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);

        // WHEN require interactive to auth is disabled, and keyguard is not awake
@@ -1401,9 +1399,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    public void notListeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthEnabled()
            throws RemoteException {
        // GIVEN SFPS supported and enrolled
        final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
        props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
        when(mAuthController.getSfpsProps()).thenReturn(props);
        when(mAuthController.isSfpsSupported()).thenReturn(true);
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);

        // GIVEN Preconditions for sfps auth to run
@@ -2843,8 +2839,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    }

    private void givenUdfpsSupported() {
        Assert.assertFalse(mFingerprintSensorProperties.isEmpty());
        when(mAuthController.getUdfpsProps()).thenReturn(mFingerprintSensorProperties);
        when(mAuthController.isUdfpsSupported()).thenReturn(true);
        Assert.assertTrue(mKeyguardUpdateMonitor.isUdfpsSupported());
    }

+68 −4
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.whenever
@@ -37,6 +38,7 @@ import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@@ -46,7 +48,9 @@ import org.mockito.MockitoAnnotations
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
    @Mock private lateinit var dumpManager: DumpManager
    @Captor private lateinit var callbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
    @Mock private lateinit var authController: AuthController
    @Captor
    private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>

    private lateinit var testScope: TestScope

@@ -59,6 +63,7 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {

        underTest =
            DeviceEntryFingerprintAuthRepositoryImpl(
                authController,
                keyguardUpdateMonitor,
                testScope.backgroundScope,
                dumpManager,
@@ -67,7 +72,7 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {

    @After
    fun tearDown() {
        verify(keyguardUpdateMonitor).removeCallback(callbackCaptor.value)
        //        verify(keyguardUpdateMonitor).removeCallback(updateMonitorCallback.value)
    }

    @Test
@@ -76,8 +81,8 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
            val isLockedOutValue = collectLastValue(underTest.isLockedOut)
            runCurrent()

            verify(keyguardUpdateMonitor).registerCallback(callbackCaptor.capture())
            val callback = callbackCaptor.value
            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
            val callback = updateMonitorCallback.value
            whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)

            callback.onLockedOutStateChanged(BiometricSourceType.FACE)
@@ -90,4 +95,63 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
            callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
            assertThat(isLockedOutValue()).isFalse()
        }

    @Test
    fun fpRunningStateIsPropagated() =
        testScope.runTest {
            val isRunning = collectLastValue(underTest.isRunning)
            whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true)

            // Initial value is available
            assertThat(isRunning()).isTrue()

            verify(keyguardUpdateMonitor, atLeastOnce())
                .registerCallback(updateMonitorCallback.capture())
            invokeOnCallback {
                it.onBiometricRunningStateChanged(false, BiometricSourceType.FINGERPRINT)
            }

            assertThat(isRunning()).isFalse()

            invokeOnCallback { it.onBiometricRunningStateChanged(true, BiometricSourceType.FACE) }

            assertThat(isRunning()).isFalse()

            updateMonitorCallback.value.onBiometricRunningStateChanged(
                true,
                BiometricSourceType.FINGERPRINT
            )
            assertThat(isRunning()).isTrue()
        }

    private fun invokeOnCallback(action: (KeyguardUpdateMonitorCallback) -> Unit) {
        updateMonitorCallback.allValues.forEach { action(it) }
    }

    @Test
    fun enabledFingerprintTypeProvidesTheCorrectOutput() =
        testScope.runTest {
            whenever(authController.isSfpsSupported).thenReturn(true)
            whenever(authController.isUdfpsSupported).thenReturn(false)
            whenever(authController.isRearFpsSupported).thenReturn(false)

            assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.SIDE_FINGERPRINT)

            whenever(authController.isSfpsSupported).thenReturn(false)
            whenever(authController.isUdfpsSupported).thenReturn(true)
            whenever(authController.isRearFpsSupported).thenReturn(false)

            assertThat(underTest.availableFpSensorType)
                .isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)

            whenever(authController.isSfpsSupported).thenReturn(false)
            whenever(authController.isUdfpsSupported).thenReturn(false)
            whenever(authController.isRearFpsSupported).thenReturn(true)

            assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.REAR_FINGERPRINT)

            whenever(authController.isRearFpsSupported).thenReturn(false)

            assertThat(underTest.availableFpSensorType).isNull()
        }
}
Loading