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.annotation.IdRes
20 import android.content.Context
21 import android.hardware.biometrics.BiometricManager.Authenticators
22 import android.hardware.biometrics.ComponentInfoInternal
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 android.os.Bundle
31 import android.testing.ViewUtils
32 import android.view.LayoutInflater
33
34 /**
35 * Inflate the given BiometricPrompt layout and initialize it with test parameters.
36 *
37 * This attaches the view so be sure to call [destroyDialog] at the end of the test.
38 */
39 @IdRes
40 internal fun <T : AuthBiometricView> Int.asTestAuthBiometricView(
41 context: Context,
42 callback: AuthBiometricView.Callback,
43 panelController: AuthPanelController,
44 allowDeviceCredential: Boolean = false,
45 savedState: Bundle? = null,
46 hideDelay: Int = 0
47 ): T {
48 val view = LayoutInflater.from(context).inflate(this, null, false) as T
49 view.mAnimationDurationLong = 0
50 view.mAnimationDurationShort = 0
51 view.mAnimationDurationHideDialog = hideDelay
52 view.setPromptInfo(buildPromptInfo(allowDeviceCredential))
53 view.setCallback(callback)
54 view.restoreState(savedState)
55 view.setPanelController(panelController)
56
57 ViewUtils.attachView(view)
58
59 return view
60 }
61
buildPromptInfonull62 private fun buildPromptInfo(allowDeviceCredential: Boolean): PromptInfo {
63 val promptInfo = PromptInfo()
64 promptInfo.title = "Title"
65 var authenticators = Authenticators.BIOMETRIC_WEAK
66 if (allowDeviceCredential) {
67 authenticators = authenticators or Authenticators.DEVICE_CREDENTIAL
68 } else {
69 promptInfo.negativeButtonText = "Negative"
70 }
71 promptInfo.authenticators = authenticators
72 return promptInfo
73 }
74
75 /** Detach the view, if needed. */
destroyDialognull76 internal fun AuthBiometricView?.destroyDialog() {
77 if (this != null && isAttachedToWindow) {
78 ViewUtils.detachView(this)
79 }
80 }
81
82 /** Create [FingerprintSensorPropertiesInternal] for a test. */
fingerprintSensorPropertiesInternalnull83 internal fun fingerprintSensorPropertiesInternal(
84 ids: List<Int> = listOf(0),
85 strong: Boolean = true,
86 ): List<FingerprintSensorPropertiesInternal> {
87 val componentInfo =
88 listOf(
89 ComponentInfoInternal(
90 "fingerprintSensor" /* componentId */,
91 "vendor/model/revision" /* hardwareVersion */,
92 "1.01" /* firmwareVersion */,
93 "00000001" /* serialNumber */,
94 "" /* softwareVersion */
95 ),
96 ComponentInfoInternal(
97 "matchingAlgorithm" /* componentId */,
98 "" /* hardwareVersion */,
99 "" /* firmwareVersion */,
100 "" /* serialNumber */,
101 "vendor/version/revision" /* softwareVersion */
102 )
103 )
104 return ids.map { id ->
105 FingerprintSensorPropertiesInternal(
106 id,
107 if (strong) SensorProperties.STRENGTH_STRONG else SensorProperties.STRENGTH_WEAK,
108 5 /* maxEnrollmentsPerUser */,
109 componentInfo,
110 FingerprintSensorProperties.TYPE_REAR,
111 false /* resetLockoutRequiresHardwareAuthToken */
112 )
113 }
114 }
115
116 /** Create [FaceSensorPropertiesInternal] for a test. */
faceSensorPropertiesInternalnull117 internal fun faceSensorPropertiesInternal(
118 ids: List<Int> = listOf(1),
119 strong: Boolean = true,
120 ): List<FaceSensorPropertiesInternal> {
121 val componentInfo =
122 listOf(
123 ComponentInfoInternal(
124 "faceSensor" /* componentId */,
125 "vendor/model/revision" /* hardwareVersion */,
126 "1.01" /* firmwareVersion */,
127 "00000001" /* serialNumber */,
128 "" /* softwareVersion */
129 ),
130 ComponentInfoInternal(
131 "matchingAlgorithm" /* componentId */,
132 "" /* hardwareVersion */,
133 "" /* firmwareVersion */,
134 "" /* serialNumber */,
135 "vendor/version/revision" /* softwareVersion */
136 )
137 )
138 return ids.map { id ->
139 FaceSensorPropertiesInternal(
140 id,
141 if (strong) SensorProperties.STRENGTH_STRONG else SensorProperties.STRENGTH_WEAK,
142 2 /* maxEnrollmentsPerUser */,
143 componentInfo,
144 FaceSensorProperties.TYPE_RGB,
145 true /* supportsFaceDetection */,
146 true /* supportsSelfIllumination */,
147 false /* resetLockoutRequiresHardwareAuthToken */
148 )
149 }
150 }
151
152 @Authenticators.Types
extractAuthenticatorTypesnull153 internal fun Collection<SensorPropertiesInternal?>.extractAuthenticatorTypes(): Int {
154 var authenticators = Authenticators.EMPTY_SET
155 mapNotNull { it?.sensorStrength }
156 .forEach { strength ->
157 authenticators =
158 authenticators or
159 when (strength) {
160 SensorProperties.STRENGTH_CONVENIENCE ->
161 Authenticators.BIOMETRIC_CONVENIENCE
162 SensorProperties.STRENGTH_WEAK -> Authenticators.BIOMETRIC_WEAK
163 SensorProperties.STRENGTH_STRONG -> Authenticators.BIOMETRIC_STRONG
164 else -> Authenticators.EMPTY_SET
165 }
166 }
167 return authenticators
168 }
169
promptInfonull170 internal fun promptInfo(
171 title: String = "title",
172 subtitle: String = "sub",
173 description: String = "desc",
174 credentialTitle: String? = "cred title",
175 credentialSubtitle: String? = "cred sub",
176 credentialDescription: String? = "cred desc",
177 negativeButton: String = "neg",
178 ): PromptInfo {
179 val info = PromptInfo()
180 info.title = title
181 info.subtitle = subtitle
182 info.description = description
183 credentialTitle?.let { info.deviceCredentialTitle = it }
184 credentialSubtitle?.let { info.deviceCredentialSubtitle = it }
185 credentialDescription?.let { info.deviceCredentialDescription = it }
186 info.negativeButtonText = negativeButton
187 return info
188 }
189