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