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