• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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