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

Commit 0698e320 authored by Chandru S's avatar Chandru S Committed by Automerger Merge Worker
Browse files

Merge "Add FP running state and FP sensor type information to...

Merge "Add FP running state and FP sensor type information to DeviceEntryFPAuthRepository" into udc-dev am: 8b884802

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22254595



Change-Id: I856599ee32cc23e3520f7a68bfd7a29fb34773cd
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 7ce98ef7 8b884802
Loading
Loading
Loading
Loading
+2 −4
Original line number Original line 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.
     * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
     */
     */
    public boolean isUdfpsSupported() {
    public boolean isUdfpsSupported() {
        return mAuthController.getUdfpsProps() != null
        return mAuthController.isUdfpsSupported();
                && !mAuthController.getUdfpsProps().isEmpty();
    }
    }


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


    /**
    /**
+30 −0
Original line number Original line Diff line number Diff line
@@ -984,6 +984,36 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
        return mSidefpsProps;
        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) {
    private String getErrorString(@Modality int modality, int error, int vendorCode) {
        switch (modality) {
        switch (modality) {
            case TYPE_FACE:
            case TYPE_FACE:
+46 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
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.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
@@ -29,6 +30,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.stateIn
@@ -37,6 +39,17 @@ import kotlinx.coroutines.flow.stateIn
interface DeviceEntryFingerprintAuthRepository {
interface DeviceEntryFingerprintAuthRepository {
    /** Whether the device entry fingerprint auth is locked out. */
    /** Whether the device entry fingerprint auth is locked out. */
    val isLockedOut: StateFlow<Boolean>
    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
class DeviceEntryFingerprintAuthRepositoryImpl
@Inject
@Inject
constructor(
constructor(
    val authController: AuthController,
    val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    @Application scope: CoroutineScope,
    @Application scope: CoroutineScope,
    dumpManager: DumpManager,
    dumpManager: DumpManager,
@@ -63,6 +77,12 @@ constructor(
        pw.println("isLockedOut=${isLockedOut.value}")
        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> =
    override val isLockedOut: StateFlow<Boolean> =
        conflatedCallbackFlow {
        conflatedCallbackFlow {
                val sendLockoutUpdate =
                val sendLockoutUpdate =
@@ -89,6 +109,32 @@ constructor(
            }
            }
            .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
            .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 {
    companion object {
        const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
        const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
    }
    }
+3 −8
Original line number Original line Diff line number Diff line
@@ -1360,9 +1360,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled()
    public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled()
            throws RemoteException {
            throws RemoteException {
        // SFPS supported and enrolled
        // SFPS supported and enrolled
        final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
        when(mAuthController.isSfpsSupported()).thenReturn(true);
        props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
        when(mAuthController.getSfpsProps()).thenReturn(props);
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);


        // WHEN require interactive to auth is disabled, and keyguard is not awake
        // 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()
    public void notListeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthEnabled()
            throws RemoteException {
            throws RemoteException {
        // GIVEN SFPS supported and enrolled
        // GIVEN SFPS supported and enrolled
        final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
        when(mAuthController.isSfpsSupported()).thenReturn(true);
        props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
        when(mAuthController.getSfpsProps()).thenReturn(props);
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);


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


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


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


@@ -46,7 +48,9 @@ import org.mockito.MockitoAnnotations
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
    @Mock private lateinit var dumpManager: DumpManager
    @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
    private lateinit var testScope: TestScope


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


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


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


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


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


            callback.onLockedOutStateChanged(BiometricSourceType.FACE)
            callback.onLockedOutStateChanged(BiometricSourceType.FACE)
@@ -90,4 +95,63 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
            callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
            callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
            assertThat(isLockedOutValue()).isFalse()
            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