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.domain.interactor 18 19 import android.hardware.fingerprint.FingerprintEnrollOptions 20 import android.hardware.fingerprint.FingerprintManager 21 import android.os.CancellationSignal 22 import android.util.Log 23 import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError 24 import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason 25 import com.android.settings.biometrics.fingerprint2.data.repository.UserRepo 26 import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.EnrollFingerprintInteractor 27 import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason 28 import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState 29 import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow 30 import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard 31 import kotlinx.coroutines.channels.awaitClose 32 import kotlinx.coroutines.channels.onFailure 33 import kotlinx.coroutines.delay 34 import kotlinx.coroutines.flow.Flow 35 import kotlinx.coroutines.flow.MutableStateFlow 36 import kotlinx.coroutines.flow.callbackFlow 37 import kotlinx.coroutines.flow.first 38 import kotlinx.coroutines.flow.update 39 40 class EnrollFingerprintInteractorImpl( 41 private val userRepo: UserRepo, 42 private val fingerprintManager: FingerprintManager, 43 private val fingerprintFlow: FingerprintFlow, 44 ) : EnrollFingerprintInteractor { 45 private val enrollRequestOutstanding = MutableStateFlow(false) 46 47 override suspend fun enroll( 48 hardwareAuthToken: ByteArray?, 49 enrollReason: EnrollReason, 50 fingerprintEnrollOptions: FingerprintEnrollOptions, 51 ): Flow<FingerEnrollState> = callbackFlow { 52 val userId = userRepo.currentUser.first() 53 // TODO (b/308456120) Improve this logic 54 if (enrollRequestOutstanding.value) { 55 Log.d(TAG, "Outstanding enroll request, waiting 150ms") 56 delay(150) 57 if (enrollRequestOutstanding.value) { 58 Log.e(TAG, "Request still present, continuing") 59 } 60 } 61 62 enrollRequestOutstanding.update { true } 63 64 var streamEnded = false 65 var totalSteps: Int? = null 66 val enrollmentCallback = 67 object : FingerprintManager.EnrollmentCallback() { 68 override fun onEnrollmentProgress(remaining: Int) { 69 // This is sort of an implementation detail, but unfortunately the API isn't 70 // very expressive. If anything we should look at changing the FingerprintManager API. 71 if (totalSteps == null) { 72 totalSteps = remaining + 1 73 } 74 75 trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error -> 76 Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error") 77 } 78 79 if (remaining == 0) { 80 streamEnded = true 81 enrollRequestOutstanding.update { false } 82 } 83 } 84 85 override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) { 86 trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error 87 -> 88 Log.d(TAG, "onEnrollmentHelp failed to send, due to $error") 89 } 90 } 91 92 override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) { 93 trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error -> 94 Log.d(TAG, "onEnrollmentError failed to send, due to $error") 95 } 96 Log.d(TAG, "onEnrollmentError($errMsgId)") 97 streamEnded = true 98 enrollRequestOutstanding.update { false } 99 } 100 101 override fun onUdfpsPointerDown(sensorId: Int) { 102 trySend(FingerEnrollState.PointerDown(sensorId)).onFailure { error -> 103 Log.d(TAG, "onUdfpsPointerDown failed to send, due to $error") 104 } 105 } 106 107 override fun onUdfpsPointerUp(sensorId: Int) { 108 trySend(FingerEnrollState.PointerUp(sensorId)).onFailure { error -> 109 Log.d(TAG, "onUdfpsPointerUp failed to send, due to $error") 110 } 111 } 112 113 override fun onUdfpsOverlayShown() { 114 trySend(FingerEnrollState.OverlayShown).onFailure { error -> 115 Log.d(TAG, "OverlayShown failed to send, due to $error") 116 } 117 } 118 119 override fun onAcquired(isAcquiredGood: Boolean) { 120 trySend(FingerEnrollState.Acquired(isAcquiredGood)).onFailure { error -> 121 Log.d(TAG, "Acquired failed to send, due to $error") 122 } 123 } 124 } 125 126 val cancellationSignal = CancellationSignal() 127 128 fingerprintManager.enroll( 129 hardwareAuthToken, 130 cancellationSignal, 131 userId, 132 enrollmentCallback, 133 enrollReason.toOriginalReason(), 134 fingerprintEnrollOptions, 135 ) 136 awaitClose { 137 // If the stream has not been ended, and the user has stopped collecting the flow 138 // before it was over, send cancel. 139 if (!streamEnded) { 140 Log.e(TAG, "Cancel is sent from settings for enroll()") 141 cancellationSignal.cancel() 142 } 143 } 144 } 145 146 companion object { 147 private const val TAG = "FingerprintEnrollStateRepository" 148 } 149 } 150