Merge "FingerprintSensorProperties refactor - data layer" into udc-dev am: af51da3111
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22530804
Change-Id: I4856b9fb5ebabf8c009c8f204ff4044099d21417
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 67d2f30..f0b9f67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.biometrics.dagger
import com.android.settingslib.udfps.UdfpsUtils
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
import com.android.systemui.biometrics.data.repository.PromptRepository
import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
@@ -41,6 +43,11 @@
@Binds
@SysUISingleton
+ fun fingerprintRepository(impl: FingerprintPropertyRepositoryImpl):
+ FingerprintPropertyRepository
+
+ @Binds
+ @SysUISingleton
fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
new file mode 100644
index 0000000..33fb36c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.fingerprint.FingerprintManager
+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.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+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
+
+/**
+ * A repository for the global state of FingerprintProperty.
+ *
+ * There is never more than one instance of the FingerprintProperty at any given time.
+ */
+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>
+
+ /** The security strength of sensor (convenience, weak, strong). */
+ val strength: StateFlow<SensorStrength>
+
+ /** The types of fingerprint sensor (rear, ultrasonic, optical, etc.). */
+ val sensorType: StateFlow<FingerprintSensorType>
+
+ /** The primary sensor location relative to the default display. */
+ val sensorLocation: StateFlow<SensorLocationInternal>
+
+ // TODO(b/251476085): don't implement until we need it, but expose alternative locations as
+ // a map of display id -> location or similar.
+ /** The sensor location relative to each physical display. */
+ // val sensorLocations<Map<String, SensorLocationInternal>>
+}
+
+@SysUISingleton
+class FingerprintPropertyRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val fingerprintManager: FingerprintManager
+) : FingerprintPropertyRepository {
+
+ override val isInitialized: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ override fun onAllAuthenticatorsRegistered(
+ sensors: List<FingerprintSensorPropertiesInternal>
+ ) {
+ if (sensors.isNotEmpty()) {
+ setProperties(sensors[0])
+ trySendWithFailureLogging(true, TAG, "initialize properties")
+ }
+ }
+ }
+ 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()
+
+ private val _strength: MutableStateFlow<SensorStrength> =
+ MutableStateFlow(SensorStrength.CONVENIENCE)
+ override val strength = _strength.asStateFlow()
+
+ private val _sensorType: MutableStateFlow<FingerprintSensorType> =
+ MutableStateFlow(FingerprintSensorType.UNKNOWN)
+ override val sensorType = _sensorType.asStateFlow()
+
+ private val _sensorLocation: MutableStateFlow<SensorLocationInternal> =
+ MutableStateFlow(SensorLocationInternal.DEFAULT)
+ override val sensorLocation = _sensorLocation.asStateFlow()
+
+ private fun setProperties(prop: FingerprintSensorPropertiesInternal) {
+ _sensorId.value = prop.sensorId
+ _strength.value = sensorStrengthIntToObject(prop.sensorStrength)
+ _sensorType.value = sensorTypeIntToObject(prop.sensorType)
+ _sensorLocation.value = prop.location
+ }
+
+ companion object {
+ private const val TAG = "FingerprintPropertyRepositoryImpl"
+ }
+}
+
+private fun sensorStrengthIntToObject(value: Int): SensorStrength {
+ return when (value) {
+ 0 -> SensorStrength.CONVENIENCE
+ 1 -> SensorStrength.WEAK
+ 2 -> SensorStrength.STRONG
+ else -> throw IllegalArgumentException("Invalid SensorStrength value: $value")
+ }
+}
+
+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")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
new file mode 100644
index 0000000..df5cefd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.shared.model
+
+import android.hardware.fingerprint.FingerprintSensorProperties
+
+/** Fingerprint sensor types. Represents [FingerprintSensorProperties.SensorType]. */
+enum class FingerprintSensorType {
+ UNKNOWN,
+ REAR,
+ UDFPS_ULTRASONIC,
+ UDFPS_OPTICAL,
+ POWER_BUTTON,
+ HOME_BUTTON,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
new file mode 100644
index 0000000..2982d0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.shared.model
+
+import android.hardware.biometrics.SensorProperties
+
+/** Fingerprint sensor security strength. Represents [SensorProperties.Strength]. */
+enum class SensorStrength {
+ CONVENIENCE,
+ WEAK,
+ STRONG,
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
new file mode 100644
index 0000000..f3a100b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+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 androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(JUnit4::class)
+class FingerprintRepositoryImplTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+ private lateinit var testScope: TestScope
+
+ @Mock private lateinit var fingerprintManager: FingerprintManager
+ private lateinit var repository: FingerprintPropertyRepositoryImpl
+
+ @Captor
+ private lateinit var fingerprintAuthenticatorsCaptor:
+ ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback.Stub>
+
+ @Before
+ fun setup() {
+ val dispatcher = StandardTestDispatcher()
+ testScope = TestScope(dispatcher)
+ repository =
+ FingerprintPropertyRepositoryImpl(testScope.backgroundScope, fingerprintManager)
+ testScope.runCurrent()
+
+ verify(fingerprintManager)
+ .addAuthenticatorsRegisteredCallback(fingerprintAuthenticatorsCaptor.capture())
+ }
+
+ @Test
+ fun initializeProperties() =
+ testScope.runTest {
+ val isInitialized = collectLastValue(repository.isInitialized)
+
+ assertDefaultProperties()
+ assertThat(isInitialized()).isFalse()
+
+ val fingerprintProps =
+ listOf(
+ FingerprintSensorPropertiesInternal(
+ 1 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ listOf<ComponentInfoInternal>(
+ ComponentInfoInternal(
+ "sensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */,
+ "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */,
+ "" /* softwareVersion */
+ )
+ ),
+ FingerprintSensorProperties.TYPE_REAR,
+ false /* halControlsIllumination */,
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ listOf<SensorLocationInternal>(
+ SensorLocationInternal(
+ "" /* displayId */,
+ 540 /* sensorLocationX */,
+ 1636 /* sensorLocationY */,
+ 130 /* sensorRadius */
+ )
+ )
+ )
+ )
+
+ fingerprintAuthenticatorsCaptor.value.onAllAuthenticatorsRegistered(fingerprintProps)
+
+ assertThat(repository.sensorId.value).isEqualTo(1)
+ assertThat(repository.strength.value).isEqualTo(SensorStrength.STRONG)
+ assertThat(repository.sensorType.value).isEqualTo(FingerprintSensorType.REAR)
+ with(repository.sensorLocation.value) {
+ assertThat(displayId).isEqualTo("")
+ assertThat(sensorLocationX).isEqualTo(540)
+ assertThat(sensorLocationY).isEqualTo(1636)
+ assertThat(sensorRadius).isEqualTo(130)
+ }
+ assertThat(isInitialized()).isTrue()
+ }
+
+ private fun assertDefaultProperties() {
+ assertThat(repository.sensorId.value).isEqualTo(-1)
+ assertThat(repository.strength.value).isEqualTo(SensorStrength.CONVENIENCE)
+ assertThat(repository.sensorType.value).isEqualTo(FingerprintSensorType.UNKNOWN)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
new file mode 100644
index 0000000..d9012a5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import android.hardware.biometrics.SensorLocationInternal
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeFingerprintPropertyRepository : FingerprintPropertyRepository {
+
+ private val _isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val isInitialized = _isInitialized.asStateFlow()
+
+ private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1)
+ override val sensorId: StateFlow<Int> = _sensorId.asStateFlow()
+
+ private val _strength: MutableStateFlow<SensorStrength> =
+ MutableStateFlow(SensorStrength.CONVENIENCE)
+ override val strength = _strength.asStateFlow()
+
+ private val _sensorType: MutableStateFlow<FingerprintSensorType> =
+ MutableStateFlow(FingerprintSensorType.UNKNOWN)
+ override val sensorType: StateFlow<FingerprintSensorType> = _sensorType.asStateFlow()
+
+ private val _sensorLocation: MutableStateFlow<SensorLocationInternal> =
+ MutableStateFlow(SensorLocationInternal.DEFAULT)
+ override val sensorLocation = _sensorLocation.asStateFlow()
+
+ fun setProperties(
+ sensorId: Int,
+ strength: SensorStrength,
+ sensorType: FingerprintSensorType,
+ sensorLocation: SensorLocationInternal
+ ) {
+ _sensorId.value = sensorId
+ _strength.value = strength
+ _sensorType.value = sensorType
+ _sensorLocation.value = sensorLocation
+ _isInitialized.value = true
+ }
+}