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

Commit 85385b9b authored by Grace Cheng's avatar Grace Cheng
Browse files

Re-do of FingerprintSensorProperties ui layer refactor

Redoing ag/23621138 to fix issues with FingerprintPropertyRepository
flow updates (sensorId, strength, sensorType, sensorLocations) but avoid
the reasons it got reverted (ag/24133165 side fps animation issues and
flickery behavior)

Bug: 291952939
Fixes: 286983554
Fixes: 276450632
Test: atest FingerprintRepositoryImplTest
Test: atest SideFpsOverlayInteractorTest
Test: (manual) Verify SFPS indicator animates normally
Test: (manual) Verify BP icon is visible
Change-Id: I0e0ce80500614af9dde6c246612386ab85d3ba9a
parent 5e475c5d
Loading
Loading
Loading
Loading
+50 −50
Original line number Diff line number Diff line
@@ -16,13 +16,19 @@

package com.android.systemui.biometrics.data.repository

import android.Manifest.permission.USE_BIOMETRIC_INTERNAL
import android.annotation.RequiresPermission
import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.biometrics.SensorProperties
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.biometrics.shared.model.toSensorStrength
import com.android.systemui.biometrics.shared.model.toSensorType
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -31,11 +37,10 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/**
 * A repository for the global state of FingerprintProperty.
@@ -44,22 +49,17 @@ import kotlinx.coroutines.flow.shareIn
 */
interface FingerprintPropertyRepository {

    /**
     * If the repository is initialized or not. Other properties are defaults until this is true.
     */
    val isInitialized: Flow<Boolean>

    /** The id of fingerprint sensor. */
    val sensorId: StateFlow<Int>
    val sensorId: Flow<Int>

    /** The security strength of sensor (convenience, weak, strong). */
    val strength: StateFlow<SensorStrength>
    val strength: Flow<SensorStrength>

    /** The types of fingerprint sensor (rear, ultrasonic, optical, etc.). */
    val sensorType: StateFlow<FingerprintSensorType>
    val sensorType: Flow<FingerprintSensorType>

    /** The sensor location relative to each physical display. */
    val sensorLocations: StateFlow<Map<String, SensorLocationInternal>>
    val sensorLocations: Flow<Map<String, SensorLocationInternal>>
}

@SysUISingleton
@@ -70,64 +70,64 @@ constructor(
    private val fingerprintManager: FingerprintManager?,
) : FingerprintPropertyRepository {

    override val isInitialized: Flow<Boolean> =
    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
    private val props: StateFlow<FingerprintSensorPropertiesInternal> =
        conflatedCallbackFlow {
                val callback =
                    object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
                        override fun onAllAuthenticatorsRegistered(
                            sensors: List<FingerprintSensorPropertiesInternal>
                        ) {
                            if (sensors.isNotEmpty()) {
                                setProperties(sensors[0])
                                trySendWithFailureLogging(true, TAG, "initialize properties")
                            if (sensors.isEmpty()) {
                                trySendWithFailureLogging(
                                    DEFAULT_PROPS,
                                    TAG,
                                    "no registered sensors, use default props"
                                )
                            } else {
                                trySendWithFailureLogging(
                                    sensors[0],
                                    TAG,
                                    "update properties on authenticators registered"
                                )
                            }
                        }
                    }
                fingerprintManager?.addAuthenticatorsRegisteredCallback(callback)
                trySendWithFailureLogging(false, TAG, "initial value defaulting to false")
                awaitClose {}
            }
            .shareIn(scope = applicationScope, started = SharingStarted.Eagerly, replay = 1)

    private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1)
    override val sensorId: StateFlow<Int> = _sensorId.asStateFlow()
            .stateIn(
                applicationScope,
                started = SharingStarted.Eagerly,
                initialValue = DEFAULT_PROPS,
            )

    private val _strength: MutableStateFlow<SensorStrength> =
        MutableStateFlow(SensorStrength.CONVENIENCE)
    override val strength = _strength.asStateFlow()
    override val sensorId: Flow<Int> = props.map { it.sensorId }

    private val _sensorType: MutableStateFlow<FingerprintSensorType> =
        MutableStateFlow(FingerprintSensorType.UNKNOWN)
    override val sensorType = _sensorType.asStateFlow()
    override val strength: Flow<SensorStrength> = props.map { it.sensorStrength.toSensorStrength() }

    private val _sensorLocations: MutableStateFlow<Map<String, SensorLocationInternal>> =
        MutableStateFlow(mapOf("" to SensorLocationInternal.DEFAULT))
    override val sensorLocations: StateFlow<Map<String, SensorLocationInternal>> =
        _sensorLocations.asStateFlow()
    override val sensorType: Flow<FingerprintSensorType> =
        props.map { it.sensorType.toSensorType() }

    private fun setProperties(prop: FingerprintSensorPropertiesInternal) {
        _sensorId.value = prop.sensorId
        _strength.value = prop.sensorStrength.toSensorStrength()
        _sensorType.value = sensorTypeIntToObject(prop.sensorType)
        _sensorLocations.value =
            prop.allLocations.associateBy { sensorLocationInternal ->
    override val sensorLocations: Flow<Map<String, SensorLocationInternal>> =
        props.map {
            it.allLocations.associateBy { sensorLocationInternal ->
                sensorLocationInternal.displayId
            }
        }

    companion object {
        private const val TAG = "FingerprintPropertyRepositoryImpl"
    }
}

private fun sensorTypeIntToObject(value: Int): FingerprintSensorType {
    return when (value) {
        0 -> FingerprintSensorType.UNKNOWN
        1 -> FingerprintSensorType.REAR
        2 -> FingerprintSensorType.UDFPS_ULTRASONIC
        3 -> FingerprintSensorType.UDFPS_OPTICAL
        4 -> FingerprintSensorType.POWER_BUTTON
        5 -> FingerprintSensorType.HOME_BUTTON
        else -> throw IllegalArgumentException("Invalid SensorType value: $value")
        private val DEFAULT_PROPS =
            FingerprintSensorPropertiesInternal(
                -1 /* sensorId */,
                SensorProperties.STRENGTH_CONVENIENCE,
                0 /* maxEnrollmentsPerUser */,
                listOf<ComponentInfoInternal>(),
                FingerprintSensorProperties.TYPE_UNKNOWN,
                false /* halControlsIllumination */,
                true /* resetLockoutRequiresHardwareAuthToken */,
                listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT)
            )
    }
}
+3 −5
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -69,7 +68,7 @@ interface PromptSelectorInteractor {
    val isConfirmationRequired: Flow<Boolean>

    /** Fingerprint sensor type */
    val sensorType: StateFlow<FingerprintSensorType>
    val sensorType: Flow<FingerprintSensorType>

    /** Use biometrics for authentication. */
    fun useBiometricsForAuthentication(
@@ -95,7 +94,7 @@ interface PromptSelectorInteractor {
class PromptSelectorInteractorImpl
@Inject
constructor(
    private val fingerprintPropertyRepository: FingerprintPropertyRepository,
    fingerprintPropertyRepository: FingerprintPropertyRepository,
    private val promptRepository: PromptRepository,
    lockPatternUtils: LockPatternUtils,
) : PromptSelectorInteractor {
@@ -147,8 +146,7 @@ constructor(
            }
        }

    override val sensorType: StateFlow<FingerprintSensorType> =
        fingerprintPropertyRepository.sensorType
    override val sensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType

    override fun useBiometricsForAuthentication(
        promptInfo: PromptInfo,
+22 −11
Original line number Diff line number Diff line
@@ -17,32 +17,43 @@
package com.android.systemui.biometrics.domain.interactor

import android.hardware.biometrics.SensorLocationInternal
import android.util.Log
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine

/** Business logic for SideFps overlay offsets. */
interface SideFpsOverlayInteractor {

    /** Get the corresponding offsets based on different displayId. */
    fun getOverlayOffsets(displayId: String): SensorLocationInternal
    /** The displayId of the current display. */
    val displayId: Flow<String>

    /** Overlay offsets corresponding to given displayId. */
    val overlayOffsets: Flow<SensorLocationInternal>

    /** Called on display changes, used to keep the display state in sync */
    fun onDisplayChanged(displayId: String)
}

@SysUISingleton
class SideFpsOverlayInteractorImpl
@Inject
constructor(private val fingerprintPropertyRepository: FingerprintPropertyRepository) :
constructor(fingerprintPropertyRepository: FingerprintPropertyRepository) :
    SideFpsOverlayInteractor {

    override fun getOverlayOffsets(displayId: String): SensorLocationInternal {
        val offsets = fingerprintPropertyRepository.sensorLocations.value
        return if (offsets.containsKey(displayId)) {
            offsets[displayId]!!
        } else {
            Log.w(TAG, "No location specified for display: $displayId")
            offsets[""]!!
    private val _displayId: MutableStateFlow<String> = MutableStateFlow("")
    override val displayId: Flow<String> = _displayId.asStateFlow()

    override val overlayOffsets: Flow<SensorLocationInternal> =
        combine(displayId, fingerprintPropertyRepository.sensorLocations) { displayId, offsets ->
            offsets[displayId] ?: SensorLocationInternal.DEFAULT
        }

    override fun onDisplayChanged(displayId: String) {
        _displayId.value = displayId
    }

    companion object {
+12 −0
Original line number Diff line number Diff line
@@ -27,3 +27,15 @@ enum class FingerprintSensorType {
    POWER_BUTTON,
    HOME_BUTTON,
}

/** Convert [this] to corresponding [FingerprintSensorType] */
fun Int.toSensorType(): FingerprintSensorType =
    when (this) {
        FingerprintSensorProperties.TYPE_UNKNOWN -> FingerprintSensorType.UNKNOWN
        FingerprintSensorProperties.TYPE_REAR -> FingerprintSensorType.REAR
        FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC -> FingerprintSensorType.UDFPS_ULTRASONIC
        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL -> FingerprintSensorType.UDFPS_OPTICAL
        FingerprintSensorProperties.TYPE_POWER_BUTTON -> FingerprintSensorType.POWER_BUTTON
        FingerprintSensorProperties.TYPE_HOME_BUTTON -> FingerprintSensorType.HOME_BUTTON
        else -> throw IllegalArgumentException("Invalid SensorType value: $this")
    }
+3 −3
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@ enum class SensorStrength {
/** Convert [this] to corresponding [SensorStrength] */
fun Int.toSensorStrength(): SensorStrength =
    when (this) {
        0 -> SensorStrength.CONVENIENCE
        1 -> SensorStrength.WEAK
        2 -> SensorStrength.STRONG
        SensorProperties.STRENGTH_CONVENIENCE -> SensorStrength.CONVENIENCE
        SensorProperties.STRENGTH_WEAK -> SensorStrength.WEAK
        SensorProperties.STRENGTH_STRONG -> SensorStrength.STRONG
        else -> throw IllegalArgumentException("Invalid SensorStrength value: $this")
    }
Loading