1 /*
<lambda>null2 * Copyright (C) 2021 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.systemui.biometrics
18
19 import android.graphics.Bitmap
20 import android.hardware.biometrics.BiometricManager.Authenticators
21 import android.hardware.biometrics.ComponentInfoInternal
22 import android.hardware.biometrics.PromptContentView
23 import android.hardware.biometrics.PromptInfo
24 import android.hardware.biometrics.SensorProperties
25 import android.hardware.biometrics.SensorPropertiesInternal
26 import android.hardware.face.FaceSensorProperties
27 import android.hardware.face.FaceSensorPropertiesInternal
28 import android.hardware.fingerprint.FingerprintSensorProperties
29 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
30 import com.android.keyguard.keyguardUpdateMonitor
31 import com.android.systemui.SysuiTestableContext
32 import com.android.systemui.biometrics.data.repository.biometricStatusRepository
33 import com.android.systemui.biometrics.shared.model.AuthenticationReason
34 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
35 import com.android.systemui.kosmos.Kosmos
36 import com.android.systemui.res.R
37 import com.android.systemui.util.mockito.whenever
38 import kotlinx.coroutines.ExperimentalCoroutinesApi
39 import kotlinx.coroutines.test.TestScope
40 import kotlinx.coroutines.test.runCurrent
41
42 /** Create [FingerprintSensorPropertiesInternal] for a test. */
43 internal fun fingerprintSensorPropertiesInternal(
44 ids: List<Int> = listOf(0),
45 strong: Boolean = true,
46 sensorType: Int = FingerprintSensorProperties.TYPE_REAR,
47 ): List<FingerprintSensorPropertiesInternal> {
48 val componentInfo =
49 listOf(
50 ComponentInfoInternal(
51 "fingerprintSensor" /* componentId */,
52 "vendor/model/revision" /* hardwareVersion */,
53 "1.01" /* firmwareVersion */,
54 "00000001" /* serialNumber */,
55 "", /* softwareVersion */
56 ),
57 ComponentInfoInternal(
58 "matchingAlgorithm" /* componentId */,
59 "" /* hardwareVersion */,
60 "" /* firmwareVersion */,
61 "" /* serialNumber */,
62 "vendor/version/revision", /* softwareVersion */
63 ),
64 )
65 return ids.map { id ->
66 FingerprintSensorPropertiesInternal(
67 id,
68 if (strong) SensorProperties.STRENGTH_STRONG else SensorProperties.STRENGTH_WEAK,
69 5 /* maxEnrollmentsPerUser */,
70 componentInfo,
71 sensorType,
72 false, /* resetLockoutRequiresHardwareAuthToken */
73 )
74 }
75 }
76
77 /** Create [FaceSensorPropertiesInternal] for a test. */
faceSensorPropertiesInternalnull78 internal fun faceSensorPropertiesInternal(
79 ids: List<Int> = listOf(1),
80 strong: Boolean = true,
81 ): List<FaceSensorPropertiesInternal> {
82 val componentInfo =
83 listOf(
84 ComponentInfoInternal(
85 "faceSensor" /* componentId */,
86 "vendor/model/revision" /* hardwareVersion */,
87 "1.01" /* firmwareVersion */,
88 "00000001" /* serialNumber */,
89 "", /* softwareVersion */
90 ),
91 ComponentInfoInternal(
92 "matchingAlgorithm" /* componentId */,
93 "" /* hardwareVersion */,
94 "" /* firmwareVersion */,
95 "" /* serialNumber */,
96 "vendor/version/revision", /* softwareVersion */
97 ),
98 )
99 return ids.map { id ->
100 FaceSensorPropertiesInternal(
101 id,
102 if (strong) SensorProperties.STRENGTH_STRONG else SensorProperties.STRENGTH_WEAK,
103 2 /* maxEnrollmentsPerUser */,
104 componentInfo,
105 FaceSensorProperties.TYPE_RGB,
106 true /* supportsFaceDetection */,
107 true /* supportsSelfIllumination */,
108 false, /* resetLockoutRequiresHardwareAuthToken */
109 )
110 }
111 }
112
113 @Authenticators.Types
extractAuthenticatorTypesnull114 internal fun Collection<SensorPropertiesInternal?>.extractAuthenticatorTypes(): Int {
115 var authenticators = Authenticators.EMPTY_SET
116 mapNotNull { it?.sensorStrength }
117 .forEach { strength ->
118 authenticators =
119 authenticators or
120 when (strength) {
121 SensorProperties.STRENGTH_CONVENIENCE ->
122 Authenticators.BIOMETRIC_CONVENIENCE
123 SensorProperties.STRENGTH_WEAK -> Authenticators.BIOMETRIC_WEAK
124 SensorProperties.STRENGTH_STRONG -> Authenticators.BIOMETRIC_STRONG
125 else -> Authenticators.EMPTY_SET
126 }
127 }
128 return authenticators
129 }
130
promptInfonull131 internal fun promptInfo(
132 logoRes: Int = -1,
133 logoBitmap: Bitmap? = null,
134 logoDescription: String? = null,
135 title: String = "title",
136 subtitle: String = "sub",
137 description: String = "desc",
138 contentView: PromptContentView? = null,
139 credentialTitle: String? = "cred title",
140 credentialSubtitle: String? = "cred sub",
141 credentialDescription: String? = "cred desc",
142 negativeButton: String = "neg",
143 ): PromptInfo {
144 val info = PromptInfo()
145 if (logoBitmap != null) {
146 info.setLogo(logoRes, logoBitmap)
147 }
148 info.logoDescription = logoDescription
149 info.title = title
150 info.subtitle = subtitle
151 info.description = description
152 info.contentView = contentView
153 credentialTitle?.let { info.deviceCredentialTitle = it }
154 credentialSubtitle?.let { info.deviceCredentialSubtitle = it }
155 credentialDescription?.let { info.deviceCredentialDescription = it }
156 info.negativeButtonText = negativeButton
157 return info
158 }
159
160 @OptIn(ExperimentalCoroutinesApi::class)
updateSfpsIndicatorRequestsnull161 internal fun TestScope.updateSfpsIndicatorRequests(
162 kosmos: Kosmos,
163 mContext: SysuiTestableContext,
164 primaryBouncerRequest: Boolean? = null,
165 alternateBouncerRequest: Boolean? = null,
166 biometricPromptRequest: Boolean? = null,
167 // TODO(b/365182034): update when rest to unlock feature is implemented
168 // progressBarShowing: Boolean? = null
169 ) {
170 biometricPromptRequest?.let { hasBiometricPromptRequest ->
171 if (hasBiometricPromptRequest) {
172 kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
173 AuthenticationReason.BiometricPromptAuthentication
174 )
175 } else {
176 kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
177 AuthenticationReason.NotRunning
178 )
179 }
180 }
181
182 primaryBouncerRequest?.let { hasPrimaryBouncerRequest ->
183 updatePrimaryBouncer(
184 kosmos,
185 mContext,
186 isShowing = hasPrimaryBouncerRequest,
187 isAnimatingAway = false,
188 fpsDetectionRunning = true,
189 isUnlockingWithFpAllowed = true,
190 )
191 }
192
193 alternateBouncerRequest?.let { hasAlternateBouncerRequest ->
194 kosmos.keyguardBouncerRepository.setAlternateVisible(hasAlternateBouncerRequest)
195 }
196
197 // TODO(b/365182034): set progress bar visibility when rest to unlock feature is implemented
198
199 runCurrent()
200 }
201
updatePrimaryBouncernull202 internal fun updatePrimaryBouncer(
203 kosmos: Kosmos,
204 mContext: SysuiTestableContext,
205 isShowing: Boolean,
206 isAnimatingAway: Boolean,
207 fpsDetectionRunning: Boolean,
208 isUnlockingWithFpAllowed: Boolean,
209 ) {
210 kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
211 kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
212 val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
213 kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
214 primaryStartDisappearAnimation
215 )
216
217 whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
218 .thenReturn(fpsDetectionRunning)
219 whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
220 .thenReturn(isUnlockingWithFpAllowed)
221 mContext.orCreateTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, true)
222 }
223