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