1 /* 2 * Copyright (C) 2022 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.hardware.biometrics.BiometricAuthenticator.TYPE_FACE 20 import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT 21 import android.hardware.biometrics.BiometricConstants 22 import android.hardware.face.FaceManager 23 import android.testing.TestableLooper 24 import android.testing.TestableLooper.RunWithLooper 25 import android.view.View 26 import androidx.test.ext.junit.runners.AndroidJUnit4 27 import androidx.test.filters.SmallTest 28 import com.android.systemui.R 29 import com.android.systemui.RoboPilotTest 30 import com.android.systemui.SysuiTestCase 31 import com.google.common.truth.Truth.assertThat 32 import org.junit.After 33 import org.junit.Before 34 import org.junit.Rule 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.Mock 38 import org.mockito.Mockito.never 39 import org.mockito.Mockito.verify 40 import org.mockito.Mockito.times 41 import org.mockito.junit.MockitoJUnit 42 43 44 @RunWith(AndroidJUnit4::class) 45 @RunWithLooper(setAsMainLooper = true) 46 @SmallTest 47 @RoboPilotTest 48 class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { 49 50 @JvmField 51 @Rule 52 var mockitoRule = MockitoJUnit.rule() 53 54 @Mock 55 private lateinit var callback: AuthBiometricView.Callback 56 57 @Mock 58 private lateinit var panelController: AuthPanelController 59 60 private lateinit var biometricView: AuthBiometricFingerprintAndFaceView 61 62 @Before setupnull63 fun setup() { 64 biometricView = R.layout.auth_biometric_fingerprint_and_face_view 65 .asTestAuthBiometricView(mContext, callback, panelController) 66 waitForIdleSync() 67 } 68 69 @After tearDownnull70 fun tearDown() { 71 biometricView.destroyDialog() 72 } 73 74 @Test fingerprintSuccessDoesNotRequireExplicitConfirmationnull75 fun fingerprintSuccessDoesNotRequireExplicitConfirmation() { 76 biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) 77 biometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT) 78 TestableLooper.get(this).moveTimeForward(1000) 79 waitForIdleSync() 80 81 assertThat(biometricView.isAuthenticated).isTrue() 82 verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) 83 } 84 85 @Test faceSuccessRequiresExplicitConfirmationnull86 fun faceSuccessRequiresExplicitConfirmation() { 87 biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) 88 biometricView.onAuthenticationSucceeded(TYPE_FACE) 89 waitForIdleSync() 90 91 assertThat(biometricView.isAuthenticated).isFalse() 92 assertThat(biometricView.isAuthenticating).isFalse() 93 assertThat(biometricView.mConfirmButton.visibility).isEqualTo(View.GONE) 94 verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) 95 96 // icon acts as confirm button 97 biometricView.mIconView.performClick() 98 TestableLooper.get(this).moveTimeForward(1000) 99 waitForIdleSync() 100 101 assertThat(biometricView.isAuthenticated).isTrue() 102 verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED) 103 } 104 105 @Test ignoresFaceErrors_faceIsNotClass3_notLockoutErrornull106 fun ignoresFaceErrors_faceIsNotClass3_notLockoutError() { 107 biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) 108 biometricView.onError(TYPE_FACE, "not a face") 109 waitForIdleSync() 110 111 assertThat(biometricView.isAuthenticating).isTrue() 112 verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR) 113 114 biometricView.onError(TYPE_FINGERPRINT, "that's a nope") 115 TestableLooper.get(this).moveTimeForward(1000) 116 waitForIdleSync() 117 118 verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) 119 } 120 121 @Test doNotIgnoresFaceErrors_faceIsClass3_notLockoutErrornull122 fun doNotIgnoresFaceErrors_faceIsClass3_notLockoutError() { 123 biometricView.isFaceClass3 = true 124 biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) 125 biometricView.onError(TYPE_FACE, "not a face") 126 waitForIdleSync() 127 128 assertThat(biometricView.isAuthenticating).isTrue() 129 verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR) 130 131 biometricView.onError(TYPE_FINGERPRINT, "that's a nope") 132 TestableLooper.get(this).moveTimeForward(1000) 133 waitForIdleSync() 134 135 verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) 136 } 137 138 @Test doNotIgnoresFaceErrors_faceIsClass3_lockoutErrornull139 fun doNotIgnoresFaceErrors_faceIsClass3_lockoutError() { 140 biometricView.isFaceClass3 = true 141 biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) 142 biometricView.onError( 143 TYPE_FACE, 144 FaceManager.getErrorString( 145 biometricView.context, 146 BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT, 147 0 /*vendorCode */ 148 ) 149 ) 150 waitForIdleSync() 151 152 assertThat(biometricView.isAuthenticating).isTrue() 153 verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) 154 155 biometricView.onError(TYPE_FINGERPRINT, "that's a nope") 156 TestableLooper.get(this).moveTimeForward(1000) 157 waitForIdleSync() 158 159 verify(callback, times(2)).onAction(AuthBiometricView.Callback.ACTION_ERROR) 160 } 161 162 waitForIdleSyncnull163 override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages() 164 } 165