1 /* <lambda>null2 * Copyright (C) 2023 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.content.Context 20 import android.content.Intent 21 import android.hardware.fingerprint.FingerprintEnrollOptions 22 import android.hardware.fingerprint.FingerprintManager 23 import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback 24 import android.hardware.fingerprint.FingerprintManager.RemovalCallback 25 import android.os.CancellationSignal 26 import android.util.Log 27 import com.android.settings.biometrics.GatekeeperPasswordProvider 28 import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository 29 import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor 30 import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason 31 import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState 32 import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel 33 import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData 34 import com.android.settings.password.ChooseLockSettingsHelper 35 import kotlin.coroutines.resume 36 import kotlin.coroutines.suspendCoroutine 37 import kotlinx.coroutines.CancellableContinuation 38 import kotlinx.coroutines.CoroutineDispatcher 39 import kotlinx.coroutines.flow.Flow 40 import kotlinx.coroutines.flow.flow 41 import kotlinx.coroutines.suspendCancellableCoroutine 42 import kotlinx.coroutines.withContext 43 44 private const val TAG = "FingerprintManagerInteractor" 45 46 class FingerprintManagerInteractorImpl( 47 applicationContext: Context, 48 private val backgroundDispatcher: CoroutineDispatcher, 49 private val fingerprintManager: FingerprintManager?, 50 fingerprintSensorRepository: FingerprintSensorRepository, 51 private val gatekeeperPasswordProvider: GatekeeperPasswordProvider, 52 private val fingerprintEnrollStateRepository: FingerprintEnrollInteractor, 53 ) : FingerprintManagerInteractor { 54 55 private val maxFingerprints = 56 applicationContext.resources.getInteger( 57 com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser 58 ) 59 private val applicationContext = applicationContext.applicationContext 60 61 override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> = 62 suspendCoroutine { 63 val callback = GenerateChallengeCallback { _, userId, challenge -> 64 val intent = Intent() 65 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gateKeeperPasswordHandle) 66 val challengeToken = 67 gatekeeperPasswordProvider.requestGatekeeperHat(intent, challenge, userId) 68 69 gatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false) 70 val p = Pair(challenge, challengeToken) 71 it.resume(p) 72 } 73 fingerprintManager?.generateChallenge(applicationContext.userId, callback) 74 } 75 76 override val enrolledFingerprints: Flow<List<FingerprintData>?> = flow { 77 emit( 78 fingerprintManager?.getEnrolledFingerprints(applicationContext.userId) 79 ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }?.toList() 80 ) 81 } 82 83 override val canEnrollFingerprints: Flow<Boolean> = flow { 84 emit( 85 fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)?.size ?: maxFingerprints < maxFingerprints 86 ) 87 } 88 89 override val sensorPropertiesInternal = fingerprintSensorRepository.fingerprintSensor 90 91 override val maxEnrollableFingerprints = flow { emit(maxFingerprints) } 92 93 override suspend fun enroll( 94 hardwareAuthToken: ByteArray?, 95 enrollReason: EnrollReason, 96 fingerprintEnrollOptions: FingerprintEnrollOptions, 97 ): Flow<FingerEnrollState> = 98 fingerprintEnrollStateRepository.enroll( 99 hardwareAuthToken, 100 enrollReason, 101 fingerprintEnrollOptions, 102 ) 103 104 override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine { 105 val callback = 106 object : RemovalCallback() { 107 override fun onRemovalError( 108 fp: android.hardware.fingerprint.Fingerprint, 109 errMsgId: Int, 110 errString: CharSequence, 111 ) { 112 it.resume(false) 113 } 114 115 override fun onRemovalSucceeded( 116 fp: android.hardware.fingerprint.Fingerprint?, 117 remaining: Int, 118 ) { 119 it.resume(true) 120 } 121 } 122 fingerprintManager?.remove( 123 android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId), 124 applicationContext.userId, 125 callback, 126 ) 127 } 128 129 override suspend fun renameFingerprint(fp: FingerprintData, newName: String) { 130 withContext(backgroundDispatcher) { 131 fingerprintManager?.rename(fp.fingerId, applicationContext.userId, newName) 132 } 133 } 134 135 override suspend fun hasSideFps(): Boolean? = suspendCancellableCoroutine { 136 it.resume(fingerprintManager?.isPowerbuttonFps) 137 } 138 139 override suspend fun authenticate(): FingerprintAuthAttemptModel = 140 suspendCancellableCoroutine { c: CancellableContinuation<FingerprintAuthAttemptModel> -> 141 val authenticationCallback = 142 object : FingerprintManager.AuthenticationCallback() { 143 144 override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { 145 super.onAuthenticationError(errorCode, errString) 146 if (c.isCompleted) { 147 Log.d(TAG, "framework sent down onAuthError after finish") 148 return 149 } 150 c.resume(FingerprintAuthAttemptModel.Error(errorCode, errString.toString())) 151 } 152 153 override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) { 154 super.onAuthenticationSucceeded(result) 155 if (c.isCompleted) { 156 Log.d(TAG, "framework sent down onAuthError after finish") 157 return 158 } 159 c.resume(FingerprintAuthAttemptModel.Success(result.fingerprint?.biometricId ?: -1)) 160 } 161 } 162 163 val cancellationSignal = CancellationSignal() 164 c.invokeOnCancellation { cancellationSignal.cancel() } 165 fingerprintManager?.authenticate( 166 null, 167 cancellationSignal, 168 authenticationCallback, 169 null, 170 applicationContext.userId, 171 ) 172 } 173 } 174