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

Commit b9d0add7 authored by Shawn Lin's avatar Shawn Lin Committed by Android (Google) Code Review
Browse files

Merge "Retrying face auth when the camera changed while face auth is running" into main

parents 9b2ce0e9 7552f13b
Loading
Loading
Loading
Loading
+18 −6
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -64,13 +65,16 @@ interface FacePropertyRepository {

    /** The current face sensor location in current device rotation */
    val sensorLocation: StateFlow<Point?>

    /** The info of current available camera. */
    val cameraInfo: StateFlow<CameraInfo?>
}

/** Describes a biometric sensor */
data class FaceSensorInfo(val id: Int, val strength: SensorStrength)

/** Data class for camera info */
private data class CameraInfo(
data class CameraInfo(
    /** The logical id of the camera */
    val cameraId: String,
    /** The physical id of the camera */
@@ -124,7 +128,7 @@ constructor(
    private val cameraInfoList: List<CameraInfo> = loadCameraInfoList()
    private var currentPhysicalCameraId: String? = null

    private val defaultSensorLocation: StateFlow<Point?> =
    override val cameraInfo: StateFlow<CameraInfo?> =
        ConflatedCallbackFlow.conflatedCallbackFlow {
                val callback =
                    object : CameraManager.AvailabilityCallback() {
@@ -142,7 +146,7 @@ constructor(
                                    physicalCameraId == it.cameraPhysicalId
                                }
                            trySendWithFailureLogging(
                                cameraInfo?.cameraLocation,
                                cameraInfo,
                                TAG,
                                "Update face sensor location to $cameraInfo."
                            )
@@ -168,7 +172,7 @@ constructor(
                                    }
                                currentPhysicalCameraId = cameraInfo?.cameraPhysicalId
                                trySendWithFailureLogging(
                                    cameraInfo?.cameraLocation,
                                    cameraInfo,
                                    TAG,
                                    "Update face sensor location to $cameraInfo."
                                )
@@ -181,8 +185,16 @@ constructor(
            .stateIn(
                applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue =
                    if (cameraInfoList.isNotEmpty()) cameraInfoList[0].cameraLocation else null
                initialValue = if (cameraInfoList.isNotEmpty()) cameraInfoList[0] else null
            )

    private val defaultSensorLocation: StateFlow<Point?> =
        cameraInfo
            .map { it?.cameraLocation }
            .stateIn(
                applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = null
            )

    override val sensorLocation: StateFlow<Point?> =
+12 −0
Original line number Diff line number Diff line
@@ -189,6 +189,18 @@ constructor(
                }
            }
            .launchIn(applicationScope)

        facePropertyRepository.cameraInfo
            .onEach {
                if (it != null && isRunning()) {
                    repository.cancel()
                    runFaceAuth(
                        FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED,
                        fallbackToDetect = true
                    )
                }
            }
            .launchIn(applicationScope)
    }

    private suspend fun resetLockedOutState(currentUserId: Int) {
+5 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALTERNATE
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.BIOMETRIC_ENABLED
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_AVAILABLE_CHANGED
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_LAUNCHED
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DISPLAY_OFF
@@ -130,6 +131,7 @@ private object InternalFaceAuthReasons {
        "Face auth stopped because non strong biometric allowed changed"
    const val POSTURE_CHANGED = "Face auth started/stopped due to device posture changed."
    const val DISPLAY_OFF = "Face auth stopped due to display state OFF."
    const val CAMERA_AVAILABLE_CHANGED = "Face auth started due to the available camera changed"
}

/**
@@ -221,7 +223,9 @@ constructor(private val id: Int, val reason: String, var extraInfo: Int = 0) :
    @UiEvent(doc = NON_STRONG_BIOMETRIC_ALLOWED_CHANGED)
    FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED(1256, NON_STRONG_BIOMETRIC_ALLOWED_CHANGED),
    @UiEvent(doc = ACCESSIBILITY_ACTION) FACE_AUTH_ACCESSIBILITY_ACTION(1454, ACCESSIBILITY_ACTION),
    @UiEvent(doc = DISPLAY_OFF) FACE_AUTH_DISPLAY_OFF(1461, DISPLAY_OFF);
    @UiEvent(doc = DISPLAY_OFF) FACE_AUTH_DISPLAY_OFF(1461, DISPLAY_OFF),
    @UiEvent(doc = CAMERA_AVAILABLE_CHANGED)
    FACE_AUTH_CAMERA_AVAILABLE_CHANGED(1623, CAMERA_AVAILABLE_CHANGED);

    override fun getId(): Int = this.id

+29 −0
Original line number Diff line number Diff line
@@ -237,6 +237,35 @@ class FacePropertyRepositoryImplTest : SysuiTestCase() {
        }
    }

    @Test
    fun providesTheCameraInfoOnCameraAvailableChange() {
        testScope.runTest {
            runCurrent()
            collectLastValue(underTest.cameraInfo)

            verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
            callback.value.onAllAuthenticatorsRegistered(
                listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
            )
            runCurrent()
            verify(cameraManager)
                .registerAvailabilityCallback(any(Executor::class.java), cameraCallback.capture())

            cameraCallback.value.onPhysicalCameraAvailable("0", PHYSICAL_CAMERA_ID_OUTER_FRONT)
            runCurrent()

            val cameraInfo by collectLastValue(underTest.cameraInfo)
            assertThat(cameraInfo)
                .isEqualTo(
                    CameraInfo(
                        "0",
                        PHYSICAL_CAMERA_ID_OUTER_FRONT,
                        Point(OUTER_FRONT_SENSOR_LOCATION[0], OUTER_FRONT_SENSOR_LOCATION[1])
                    )
                )
        }
    }

    private fun createSensorProperties(id: Int, strength: Int) =
        FaceSensorPropertiesInternal(id, strength, 0, emptyList(), 1, false, false, false)
}
+42 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.keyguard.trustManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.CameraInfo
import com.android.systemui.biometrics.data.repository.FaceSensorInfo
import com.android.systemui.biometrics.data.repository.facePropertyRepository
import com.android.systemui.biometrics.shared.model.LockoutMode
@@ -490,6 +491,47 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
            verify(trustManager).clearAllBiometricRecognized(eq(BiometricSourceType.FACE), anyInt())
        }

    @Test
    fun faceAuthIsRequestedWhenAuthIsRunningWhileCameraInfoChanged() =
        testScope.runTest {
            facePropertyRepository.setCameraIno(null)
            underTest.start()

            faceAuthRepository.requestAuthenticate(
                FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
                true
            )
            facePropertyRepository.setCameraIno(CameraInfo("0", "1", null))

            runCurrent()
            assertThat(faceAuthRepository.runningAuthRequest.value)
                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED, true))
        }

    @Test
    fun faceAuthIsNotRequestedWhenNoAuthRunningWhileCameraInfoChanged() =
        testScope.runTest {
            facePropertyRepository.setCameraIno(null)
            underTest.start()

            facePropertyRepository.setCameraIno(CameraInfo("0", "1", null))

            runCurrent()
            assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
        }

    @Test
    fun faceAuthIsNotRequestedWhenAuthIsRunningWhileCameraInfoIsNull() =
        testScope.runTest {
            facePropertyRepository.setCameraIno(null)
            underTest.start()

            facePropertyRepository.setCameraIno(null)

            runCurrent()
            assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
        }

    companion object {
        private const val primaryUserId = 1
        private val primaryUser = UserInfo(primaryUserId, "test user", UserInfo.FLAG_PRIMARY)
Loading