1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.biometrics.fingerprint2.data.repository 18 19 import android.hardware.biometrics.BiometricStateListener 20 import android.hardware.fingerprint.FingerprintManager 21 import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData 22 import kotlinx.coroutines.CoroutineDispatcher 23 import kotlinx.coroutines.CoroutineScope 24 import kotlinx.coroutines.channels.awaitClose 25 import kotlinx.coroutines.flow.Flow 26 import kotlinx.coroutines.flow.MutableStateFlow 27 import kotlinx.coroutines.flow.SharingStarted 28 import kotlinx.coroutines.flow.asStateFlow 29 import kotlinx.coroutines.flow.callbackFlow 30 import kotlinx.coroutines.flow.combine 31 import kotlinx.coroutines.flow.distinctUntilChanged 32 import kotlinx.coroutines.flow.filterNotNull 33 import kotlinx.coroutines.flow.flowOn 34 import kotlinx.coroutines.flow.stateIn 35 import kotlinx.coroutines.flow.update 36 import kotlinx.coroutines.withContext 37 38 /** Repository that contains information about fingerprint enrollments. */ 39 interface FingerprintEnrollmentRepository { 40 /** The current enrollments of the user */ 41 val currentEnrollments: Flow<List<FingerprintData>?> 42 43 /** Indicates the maximum fingerprints that are enrollable * */ 44 val maxFingerprintsEnrollable: Flow<Int> 45 46 /** Indicates if a user can enroll another fingerprint */ 47 val canEnrollUser: Flow<Boolean> 48 49 val enrollStageCount: Int 50 51 /** 52 * Returns the threshold for the given stage of fingerprint enrollment. 53 * 54 * @param index The index of the enrollment stage. 55 * @return The threshold for the enrollment stage. 56 */ 57 fun getEnrollStageThreshold(index: Int): Float 58 59 /** 60 * Indicates if we should use the default settings for maximum enrollments or the sensor props 61 * from the fingerprint sensor 62 */ 63 fun setShouldUseSettingsMaxFingerprints(useSettings: Boolean) 64 } 65 66 class FingerprintEnrollmentRepositoryImpl( 67 private val fingerprintManager: FingerprintManager, 68 userRepo: UserRepo, 69 settingsRepository: FingerprintSettingsRepository, 70 backgroundDispatcher: CoroutineDispatcher, 71 applicationScope: CoroutineScope, 72 sensorRepo: FingerprintSensorRepository, 73 ) : FingerprintEnrollmentRepository { 74 75 private val _shouldUseSettingsMaxFingerprints = MutableStateFlow(false) 76 val shouldUseSettingsMaxFingerprints = _shouldUseSettingsMaxFingerprints.asStateFlow() 77 78 private val enrollmentChangedFlow: Flow<Int?> = <lambda>null79 callbackFlow { 80 val callback = 81 object : BiometricStateListener() { 82 override fun onEnrollmentsChanged(userId: Int, sensorId: Int, hasEnrollments: Boolean) { 83 trySend(userId) 84 } 85 } 86 withContext(backgroundDispatcher) { 87 fingerprintManager.registerBiometricStateListener(callback) 88 } 89 awaitClose { 90 // no way to unregister 91 } 92 } 93 .stateIn(applicationScope, started = SharingStarted.Eagerly, initialValue = null) 94 95 override val currentEnrollments: Flow<List<FingerprintData>> = 96 userRepo.currentUser 97 .distinctUntilChanged() currentUsernull98 .combine(enrollmentChangedFlow) { currentUser, _ -> getFingerprintsForUser(currentUser) } 99 .filterNotNull() 100 .flowOn(backgroundDispatcher) 101 102 override val maxFingerprintsEnrollable: Flow<Int> = 103 shouldUseSettingsMaxFingerprints.combine(sensorRepo.fingerprintSensor) { shouldUseSettingsnull104 shouldUseSettings, 105 sensor -> 106 if (shouldUseSettings) { 107 settingsRepository.maxEnrollableFingerprints() 108 } else { 109 sensor.maxEnrollmentsPerUser 110 } 111 } 112 113 override val canEnrollUser: Flow<Boolean> = enrollmentsnull114 currentEnrollments.combine(maxFingerprintsEnrollable) { enrollments, maxFingerprints -> 115 enrollments.size < maxFingerprints 116 } 117 setShouldUseSettingsMaxFingerprintsnull118 override fun setShouldUseSettingsMaxFingerprints(useSettings: Boolean) { 119 _shouldUseSettingsMaxFingerprints.update { useSettings } 120 } 121 getFingerprintsForUsernull122 private fun getFingerprintsForUser(userId: Int): List<FingerprintData>? { 123 return fingerprintManager 124 .getEnrolledFingerprints(userId) 125 ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) } 126 ?.toList() 127 } 128 129 override val enrollStageCount: Int 130 get() = fingerprintManager.enrollStageCount 131 getEnrollStageThresholdnull132 override fun getEnrollStageThreshold(index: Int): Float = 133 fingerprintManager.getEnrollStageThreshold(index) 134 } 135