1 /* 2 * Copyright (C) 2019 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 static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; 20 import static android.hardware.biometrics.BiometricManager.Authenticators; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static junit.framework.Assert.assertEquals; 25 import static junit.framework.Assert.assertFalse; 26 import static junit.framework.Assert.assertNotSame; 27 import static junit.framework.Assert.assertNull; 28 import static junit.framework.Assert.assertTrue; 29 30 import static org.mockito.ArgumentMatchers.any; 31 import static org.mockito.ArgumentMatchers.anyInt; 32 import static org.mockito.ArgumentMatchers.anyLong; 33 import static org.mockito.ArgumentMatchers.anyString; 34 import static org.mockito.ArgumentMatchers.eq; 35 import static org.mockito.Mockito.doAnswer; 36 import static org.mockito.Mockito.doReturn; 37 import static org.mockito.Mockito.mock; 38 import static org.mockito.Mockito.never; 39 import static org.mockito.Mockito.reset; 40 import static org.mockito.Mockito.same; 41 import static org.mockito.Mockito.spy; 42 import static org.mockito.Mockito.verify; 43 import static org.mockito.Mockito.when; 44 45 import android.app.ActivityManager; 46 import android.app.ActivityTaskManager; 47 import android.content.ComponentName; 48 import android.content.Context; 49 import android.content.Intent; 50 import android.content.pm.PackageManager; 51 import android.content.res.Configuration; 52 import android.content.res.Resources; 53 import android.graphics.Point; 54 import android.hardware.biometrics.BiometricAuthenticator; 55 import android.hardware.biometrics.BiometricConstants; 56 import android.hardware.biometrics.BiometricPrompt; 57 import android.hardware.biometrics.BiometricStateListener; 58 import android.hardware.biometrics.ComponentInfoInternal; 59 import android.hardware.biometrics.IBiometricContextListener; 60 import android.hardware.biometrics.IBiometricSysuiReceiver; 61 import android.hardware.biometrics.PromptInfo; 62 import android.hardware.biometrics.SensorProperties; 63 import android.hardware.display.DisplayManager; 64 import android.hardware.face.FaceManager; 65 import android.hardware.face.FaceSensorProperties; 66 import android.hardware.face.FaceSensorPropertiesInternal; 67 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; 68 import android.hardware.fingerprint.FingerprintManager; 69 import android.hardware.fingerprint.FingerprintSensorProperties; 70 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; 71 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; 72 import android.os.Bundle; 73 import android.os.Handler; 74 import android.os.RemoteException; 75 import android.os.UserManager; 76 import android.testing.TestableContext; 77 import android.testing.TestableLooper; 78 import android.testing.TestableLooper.RunWithLooper; 79 import android.view.DisplayInfo; 80 import android.view.Surface; 81 import android.view.WindowManager; 82 83 import androidx.test.ext.junit.runners.AndroidJUnit4; 84 import androidx.test.filters.SmallTest; 85 86 import com.android.internal.R; 87 import com.android.internal.jank.InteractionJankMonitor; 88 import com.android.internal.widget.LockPatternUtils; 89 import com.android.settingslib.udfps.UdfpsUtils; 90 import com.android.systemui.RoboPilotTest; 91 import com.android.systemui.SysuiTestCase; 92 import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; 93 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; 94 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; 95 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; 96 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; 97 import com.android.systemui.flags.FakeFeatureFlags; 98 import com.android.systemui.flags.Flags; 99 import com.android.systemui.keyguard.WakefulnessLifecycle; 100 import com.android.systemui.statusbar.CommandQueue; 101 import com.android.systemui.statusbar.VibratorHelper; 102 import com.android.systemui.util.concurrency.DelayableExecutor; 103 import com.android.systemui.util.concurrency.Execution; 104 import com.android.systemui.util.concurrency.FakeExecution; 105 import com.android.systemui.util.concurrency.FakeExecutor; 106 import com.android.systemui.util.time.FakeSystemClock; 107 108 import org.junit.Before; 109 import org.junit.Rule; 110 import org.junit.Test; 111 import org.junit.runner.RunWith; 112 import org.mockito.AdditionalMatchers; 113 import org.mockito.ArgumentCaptor; 114 import org.mockito.Captor; 115 import org.mockito.Mock; 116 import org.mockito.junit.MockitoJUnit; 117 import org.mockito.junit.MockitoRule; 118 119 import java.util.ArrayList; 120 import java.util.List; 121 import java.util.Random; 122 123 @RunWith(AndroidJUnit4.class) 124 @RunWithLooper 125 @SmallTest 126 @RoboPilotTest 127 public class AuthControllerTest extends SysuiTestCase { 128 129 private static final long REQUEST_ID = 22; 130 131 @Rule 132 public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 133 134 @Mock 135 private PackageManager mPackageManager; 136 @Mock 137 private IBiometricSysuiReceiver mReceiver; 138 @Mock 139 private IBiometricContextListener mContextListener; 140 @Mock 141 private AuthDialog mDialog1; 142 @Mock 143 private AuthDialog mDialog2; 144 @Mock 145 private CommandQueue mCommandQueue; 146 @Mock 147 private ActivityTaskManager mActivityTaskManager; 148 @Mock 149 private WindowManager mWindowManager; 150 @Mock 151 private FingerprintManager mFingerprintManager; 152 @Mock 153 private FaceManager mFaceManager; 154 @Mock 155 private UdfpsController mUdfpsController; 156 @Mock 157 private SideFpsController mSideFpsController; 158 @Mock 159 private DisplayManager mDisplayManager; 160 @Mock 161 private WakefulnessLifecycle mWakefulnessLifecycle; 162 @Mock 163 private AuthDialogPanelInteractionDetector mPanelInteractionDetector; 164 @Mock 165 private UserManager mUserManager; 166 @Mock 167 private LockPatternUtils mLockPatternUtils; 168 @Mock 169 private LogContextInteractor mLogContextInteractor; 170 @Mock 171 private UdfpsLogger mUdfpsLogger; 172 @Mock 173 private InteractionJankMonitor mInteractionJankMonitor; 174 @Mock 175 private PromptCredentialInteractor mBiometricPromptCredentialInteractor; 176 @Mock 177 private PromptSelectorInteractor mPromptSelectionInteractor; 178 @Mock 179 private CredentialViewModel mCredentialViewModel; 180 @Mock 181 private PromptViewModel mPromptViewModel; 182 @Mock 183 private UdfpsUtils mUdfpsUtils; 184 185 @Captor 186 private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor; 187 @Captor 188 private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; 189 @Captor 190 private ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor; 191 @Captor 192 private ArgumentCaptor<Integer> mModalityCaptor; 193 @Captor 194 private ArgumentCaptor<String> mMessageCaptor; 195 @Mock 196 private Resources mResources; 197 @Mock 198 private VibratorHelper mVibratorHelper; 199 200 private TestableContext mContextSpy; 201 private Execution mExecution; 202 private TestableLooper mTestableLooper; 203 private Handler mHandler; 204 private DelayableExecutor mBackgroundExecutor; 205 private TestableAuthController mAuthController; 206 private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); 207 208 @Before setup()209 public void setup() throws RemoteException { 210 // TODO(b/278622168): remove with flag 211 // AuthController simply passes this through to AuthContainerView (does not impact test) 212 mFeatureFlags.set(Flags.BIOMETRIC_BP_STRONG, false); 213 214 mContextSpy = spy(mContext); 215 mExecution = new FakeExecution(); 216 mTestableLooper = TestableLooper.get(this); 217 mHandler = new Handler(mTestableLooper.getLooper()); 218 mBackgroundExecutor = new FakeExecutor(new FakeSystemClock()); 219 220 when(mContextSpy.getPackageManager()).thenReturn(mPackageManager); 221 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) 222 .thenReturn(true); 223 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) 224 .thenReturn(true); 225 226 when(mDialog1.getOpPackageName()).thenReturn("Dialog1"); 227 when(mDialog2.getOpPackageName()).thenReturn("Dialog2"); 228 229 when(mDialog1.isAllowDeviceCredentials()).thenReturn(false); 230 when(mDialog2.isAllowDeviceCredentials()).thenReturn(false); 231 232 when(mDialog1.getRequestId()).thenReturn(REQUEST_ID); 233 when(mDialog2.getRequestId()).thenReturn(REQUEST_ID); 234 235 when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point()); 236 237 when(mFingerprintManager.isHardwareDetected()).thenReturn(true); 238 when(mFaceManager.isHardwareDetected()).thenReturn(true); 239 240 final List<ComponentInfoInternal> fpComponentInfo = List.of( 241 new ComponentInfoInternal("faceSensor" /* componentId */, 242 "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, 243 "00000001" /* serialNumber */, "" /* softwareVersion */)); 244 final List<ComponentInfoInternal> faceComponentInfo = List.of( 245 new ComponentInfoInternal("matchingAlgorithm" /* componentId */, 246 "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, 247 "vendor/version/revision" /* softwareVersion */)); 248 249 final List<FingerprintSensorPropertiesInternal> fpProps = List.of( 250 new FingerprintSensorPropertiesInternal( 251 1 /* sensorId */, 252 SensorProperties.STRENGTH_STRONG, 253 1 /* maxEnrollmentsPerUser */, 254 fpComponentInfo, 255 FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, 256 true /* resetLockoutRequireHardwareAuthToken */)); 257 when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(fpProps); 258 259 final List<FaceSensorPropertiesInternal> faceProps = List.of( 260 new FaceSensorPropertiesInternal( 261 2 /* sensorId */, 262 SensorProperties.STRENGTH_STRONG, 263 1 /* maxEnrollmentsPerUser */, 264 faceComponentInfo, 265 FaceSensorProperties.TYPE_RGB, 266 true /* supportsFaceDetection */, 267 true /* supportsSelfIllumination */, 268 true /* resetLockoutRequireHardwareAuthToken */)); 269 when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps); 270 271 mAuthController = new TestableAuthController(mContextSpy); 272 273 mAuthController.start(); 274 verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( 275 mFpAuthenticatorsRegisteredCaptor.capture()); 276 verify(mFaceManager).addAuthenticatorsRegisteredCallback( 277 mFaceAuthenticatorsRegisteredCaptor.capture()); 278 279 mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps); 280 mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps); 281 282 // Ensures that the operations posted on the handler get executed. 283 waitForIdleSync(); 284 } 285 286 // Callback tests 287 288 @Test testRegistersBiometricStateListener_afterAllAuthenticatorsAreRegistered()289 public void testRegistersBiometricStateListener_afterAllAuthenticatorsAreRegistered() 290 throws RemoteException { 291 // This test is sensitive to prior FingerprintManager interactions. 292 reset(mFingerprintManager); 293 reset(mFaceManager); 294 295 // This test requires an uninitialized AuthController. 296 AuthController authController = new TestableAuthController(mContextSpy); 297 authController.start(); 298 299 verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( 300 mFpAuthenticatorsRegisteredCaptor.capture()); 301 verify(mFaceManager).addAuthenticatorsRegisteredCallback( 302 mFaceAuthenticatorsRegisteredCaptor.capture()); 303 waitForIdleSync(); 304 305 verify(mFingerprintManager, never()).registerBiometricStateListener(any()); 306 verify(mFaceManager, never()).registerBiometricStateListener(any()); 307 308 mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); 309 mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); 310 waitForIdleSync(); 311 312 verify(mFingerprintManager).registerBiometricStateListener(any()); 313 verify(mFaceManager).registerBiometricStateListener(any()); 314 } 315 316 @Test testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor()317 public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException { 318 // This test is sensitive to prior FingerprintManager interactions. 319 reset(mFingerprintManager); 320 reset(mFaceManager); 321 322 // This test requires an uninitialized AuthController. 323 AuthController authController = new TestableAuthController(mContextSpy); 324 authController.start(); 325 326 verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( 327 mFpAuthenticatorsRegisteredCaptor.capture()); 328 verify(mFaceManager).addAuthenticatorsRegisteredCallback( 329 mFaceAuthenticatorsRegisteredCaptor.capture()); 330 331 // Emulates a device with no authenticators (empty list). 332 mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); 333 mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); 334 waitForIdleSync(); 335 336 verify(mFingerprintManager).registerBiometricStateListener( 337 mBiometricStateCaptor.capture()); 338 verify(mFaceManager).registerBiometricStateListener( 339 mBiometricStateCaptor.capture()); 340 341 // Enrollments changed for an unknown sensor. 342 for (BiometricStateListener listener : mBiometricStateCaptor.getAllValues()) { 343 listener.onEnrollmentsChanged(0 /* userId */, 344 0xbeef /* sensorId */, true /* hasEnrollments */); 345 } 346 waitForIdleSync(); 347 348 // Nothing should crash. 349 } 350 351 @Test testFaceAuthEnrollmentStatus()352 public void testFaceAuthEnrollmentStatus() throws RemoteException { 353 final int userId = 0; 354 355 reset(mFaceManager); 356 mAuthController.start(); 357 358 verify(mFaceManager).addAuthenticatorsRegisteredCallback( 359 mFaceAuthenticatorsRegisteredCaptor.capture()); 360 361 mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered( 362 mFaceManager.getSensorPropertiesInternal()); 363 mTestableLooper.processAllMessages(); 364 365 verify(mFaceManager).registerBiometricStateListener( 366 mBiometricStateCaptor.capture()); 367 368 assertFalse(mAuthController.isFaceAuthEnrolled(userId)); 369 370 // Enrollments changed for an unknown sensor. 371 for (BiometricStateListener listener : mBiometricStateCaptor.getAllValues()) { 372 listener.onEnrollmentsChanged(userId, 373 2 /* sensorId */, true /* hasEnrollments */); 374 } 375 mTestableLooper.processAllMessages(); 376 377 assertTrue(mAuthController.isFaceAuthEnrolled(userId)); 378 } 379 380 381 @Test testSendsReasonUserCanceled_whenDismissedByUserCancel()382 public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception { 383 showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); 384 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, 385 null, /* credentialAttestation */ 386 mAuthController.mCurrentDialog.getRequestId()); 387 verify(mReceiver).onDialogDismissed( 388 eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), 389 eq(null) /* credentialAttestation */); 390 } 391 392 @Test testSendsReasonNegative_whenDismissedByButtonNegative()393 public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception { 394 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 395 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE, 396 null, /* credentialAttestation */ 397 mAuthController.mCurrentDialog.getRequestId()); 398 verify(mReceiver).onDialogDismissed( 399 eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE), 400 eq(null) /* credentialAttestation */); 401 } 402 403 @Test testSendsReasonConfirmed_whenDismissedByButtonPositive()404 public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception { 405 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 406 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE, 407 null, /* credentialAttestation */ 408 mAuthController.mCurrentDialog.getRequestId()); 409 verify(mReceiver).onDialogDismissed( 410 eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED), 411 eq(null) /* credentialAttestation */); 412 } 413 414 @Test testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated()415 public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception { 416 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 417 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED, 418 null, /* credentialAttestation */ 419 mAuthController.mCurrentDialog.getRequestId()); 420 verify(mReceiver).onDialogDismissed( 421 eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED), 422 eq(null) /* credentialAttestation */); 423 } 424 425 @Test testSendsReasonError_whenDismissedByError()426 public void testSendsReasonError_whenDismissedByError() throws Exception { 427 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 428 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR, 429 null, /* credentialAttestation */ 430 mAuthController.mCurrentDialog.getRequestId()); 431 verify(mReceiver).onDialogDismissed( 432 eq(BiometricPrompt.DISMISSED_REASON_ERROR), 433 eq(null) /* credentialAttestation */); 434 } 435 436 @Test testSendsReasonServerRequested_whenDismissedByServer()437 public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception { 438 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 439 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER, 440 null, /* credentialAttestation */ 441 mAuthController.mCurrentDialog.getRequestId()); 442 verify(mReceiver).onDialogDismissed( 443 eq(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED), 444 eq(null) /* credentialAttestation */); 445 } 446 447 @Test testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated()448 public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated() 449 throws Exception { 450 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 451 452 final byte[] credentialAttestation = generateRandomHAT(); 453 454 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, 455 credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); 456 verify(mReceiver).onDialogDismissed( 457 eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), 458 AdditionalMatchers.aryEq(credentialAttestation)); 459 } 460 461 // Statusbar tests 462 463 @Test testShowInvoked_whenSystemRequested()464 public void testShowInvoked_whenSystemRequested() { 465 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 466 verify(mDialog1).show(any(), any()); 467 } 468 469 @Test testOnAuthenticationSucceededInvoked_whenSystemRequested()470 public void testOnAuthenticationSucceededInvoked_whenSystemRequested() { 471 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 472 mAuthController.onBiometricAuthenticated(TYPE_FINGERPRINT); 473 verify(mDialog1).onAuthenticationSucceeded(eq(TYPE_FINGERPRINT)); 474 } 475 476 @Test testOnAuthenticationFailedInvoked_whenBiometricRejected()477 public void testOnAuthenticationFailedInvoked_whenBiometricRejected() { 478 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 479 final int modality = BiometricAuthenticator.TYPE_NONE; 480 mAuthController.onBiometricError(modality, 481 BiometricConstants.BIOMETRIC_PAUSED_REJECTED, 482 0 /* vendorCode */); 483 484 verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(), mMessageCaptor.capture()); 485 486 assertEquals(mModalityCaptor.getValue().intValue(), modality); 487 assertEquals(mMessageCaptor.getValue(), 488 mContext.getString(R.string.biometric_not_recognized)); 489 } 490 491 @Test testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withPaused()492 public void testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withPaused() { 493 testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected( 494 BiometricConstants.BIOMETRIC_PAUSED_REJECTED); 495 } 496 497 @Test testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withTimeout()498 public void testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withTimeout() { 499 testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected( 500 BiometricConstants.BIOMETRIC_ERROR_TIMEOUT); 501 } 502 testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected(int error)503 private void testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected(int error) { 504 final int modality = BiometricAuthenticator.TYPE_FACE; 505 final int userId = 0; 506 507 enrollFingerprintAndFace(userId); 508 509 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 510 511 mAuthController.onBiometricError(modality, error, 0 /* vendorCode */); 512 513 verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(), mMessageCaptor.capture()); 514 515 assertThat(mModalityCaptor.getValue().intValue()).isEqualTo(modality); 516 assertThat(mMessageCaptor.getValue()).isEqualTo( 517 mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)); 518 } 519 520 @Test testOnAuthenticationFailedInvoked_whenFingerprintAuthRejected()521 public void testOnAuthenticationFailedInvoked_whenFingerprintAuthRejected() { 522 final int modality = BiometricAuthenticator.TYPE_FINGERPRINT; 523 final int userId = 0; 524 525 enrollFingerprintAndFace(userId); 526 527 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 528 529 mAuthController.onBiometricError(modality, 530 BiometricConstants.BIOMETRIC_PAUSED_REJECTED, 531 0 /* vendorCode */); 532 533 verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(), mMessageCaptor.capture()); 534 535 assertThat(mModalityCaptor.getValue().intValue()).isEqualTo(modality); 536 assertThat(mMessageCaptor.getValue()).isEqualTo( 537 mContext.getString(R.string.fingerprint_error_not_match)); 538 } 539 540 @Test testOnAuthenticationFailedInvoked_whenBiometricTimedOut()541 public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() { 542 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 543 final int modality = BiometricAuthenticator.TYPE_FACE; 544 final int error = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT; 545 final int vendorCode = 0; 546 mAuthController.onBiometricError(modality, error, vendorCode); 547 548 verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(), mMessageCaptor.capture()); 549 550 assertThat(mModalityCaptor.getValue().intValue()).isEqualTo(modality); 551 assertThat(mMessageCaptor.getValue()).isEqualTo( 552 mContext.getString(R.string.biometric_not_recognized)); 553 } 554 555 @Test testOnHelpInvoked_whenSystemRequested()556 public void testOnHelpInvoked_whenSystemRequested() { 557 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 558 final int modality = BiometricAuthenticator.TYPE_IRIS; 559 final String helpMessage = "help"; 560 mAuthController.onBiometricHelp(modality, helpMessage); 561 562 verify(mDialog1).onHelp(mModalityCaptor.capture(), mMessageCaptor.capture()); 563 564 assertThat(mModalityCaptor.getValue().intValue()).isEqualTo(modality); 565 assertThat(mMessageCaptor.getValue()).isEqualTo(helpMessage); 566 } 567 568 @Test testOnErrorInvoked_whenSystemRequested()569 public void testOnErrorInvoked_whenSystemRequested() { 570 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 571 final int modality = BiometricAuthenticator.TYPE_FACE; 572 final int error = 1; 573 final int vendorCode = 0; 574 mAuthController.onBiometricError(modality, error, vendorCode); 575 576 verify(mDialog1).onError(mModalityCaptor.capture(), mMessageCaptor.capture()); 577 578 assertThat(mModalityCaptor.getValue().intValue()).isEqualTo(modality); 579 assertThat(mMessageCaptor.getValue()).isEqualTo( 580 FaceManager.getErrorString(mContext, error, vendorCode)); 581 } 582 583 @Test testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI()584 public void testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI() { 585 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 586 final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT; 587 final int vendorCode = 0; 588 589 when(mDialog1.isAllowDeviceCredentials()).thenReturn(true); 590 591 mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); 592 verify(mDialog1, never()).onError(anyInt(), anyString()); 593 verify(mDialog1).animateToCredentialUI(eq(true)); 594 } 595 596 @Test testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI()597 public void testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI() { 598 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 599 final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; 600 final int vendorCode = 0; 601 602 when(mDialog1.isAllowDeviceCredentials()).thenReturn(true); 603 604 mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode); 605 verify(mDialog1, never()).onError(anyInt(), anyString()); 606 verify(mDialog1).animateToCredentialUI(eq(true)); 607 } 608 609 @Test testErrorLockout_whenCredentialNotAllowed_sendsOnError()610 public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() { 611 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 612 final int modality = BiometricAuthenticator.TYPE_FACE; 613 final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT; 614 final int vendorCode = 0; 615 616 when(mDialog1.isAllowDeviceCredentials()).thenReturn(false); 617 618 mAuthController.onBiometricError(modality, error, vendorCode); 619 verify(mDialog1).onError( 620 eq(modality), eq(FaceManager.getErrorString(mContext, error, vendorCode))); 621 verify(mDialog1, never()).animateToCredentialUI(eq(true)); 622 } 623 624 @Test testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError()625 public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() { 626 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 627 final int modality = BiometricAuthenticator.TYPE_FACE; 628 final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT; 629 final int vendorCode = 0; 630 631 when(mDialog1.isAllowDeviceCredentials()).thenReturn(false); 632 633 mAuthController.onBiometricError(modality, error, vendorCode); 634 verify(mDialog1).onError( 635 eq(modality), eq(FaceManager.getErrorString(mContext, error, vendorCode))); 636 verify(mDialog1, never()).animateToCredentialUI(eq(true)); 637 } 638 639 @Test testHideAuthenticationDialog_invokesDismissFromSystemServer()640 public void testHideAuthenticationDialog_invokesDismissFromSystemServer() { 641 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 642 643 mAuthController.hideAuthenticationDialog(REQUEST_ID + 1); 644 verify(mDialog1, never()).dismissFromSystemServer(); 645 assertThat(mAuthController.mCurrentDialog).isSameInstanceAs(mDialog1); 646 647 mAuthController.hideAuthenticationDialog(REQUEST_ID); 648 verify(mDialog1).dismissFromSystemServer(); 649 650 // In this case, BiometricService sends the error to the client immediately, without 651 // doing a round trip to SystemUI. 652 assertNull(mAuthController.mCurrentDialog); 653 } 654 655 // Corner case tests 656 657 @Test testCancelAuthentication_whenCredentialConfirmed_doesntCrash()658 public void testCancelAuthentication_whenCredentialConfirmed_doesntCrash() throws Exception { 659 // It's possible that before the client is notified that credential is confirmed, the client 660 // requests to cancel authentication. 661 // 662 // Test that the following sequence of events does not crash SystemUI: 663 // 1) Credential is confirmed 664 // 2) Client cancels authentication 665 666 showDialog(new int[0] /* sensorIds */, true /* credentialAllowed */); 667 verify(mDialog1).show(any(), any()); 668 669 final byte[] credentialAttestation = generateRandomHAT(); 670 671 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, 672 credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); 673 verify(mReceiver).onDialogDismissed( 674 eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), 675 AdditionalMatchers.aryEq(credentialAttestation)); 676 677 mAuthController.hideAuthenticationDialog(REQUEST_ID); 678 } 679 680 @Test testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations()681 public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() { 682 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 683 verify(mDialog1).show(any(), any()); 684 685 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 686 687 // First dialog should be dismissed without animation 688 verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */); 689 690 // Second dialog should be shown without animation 691 verify(mDialog2).show(any(), any()); 692 } 693 694 @Test testConfigurationPersists_whenOnConfigurationChanged()695 public void testConfigurationPersists_whenOnConfigurationChanged() { 696 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 697 verify(mDialog1).show(any(), any()); 698 699 // Return that the UI is in "showing" state 700 doAnswer(invocation -> { 701 Object[] args = invocation.getArguments(); 702 Bundle savedState = (Bundle) args[0]; 703 savedState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false); 704 return null; // onSaveState returns void 705 }).when(mDialog1).onSaveState(any()); 706 707 mAuthController.onConfigurationChanged(new Configuration()); 708 709 ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class); 710 verify(mDialog1).onSaveState(captor.capture()); 711 712 // Old dialog doesn't animate 713 verify(mDialog1).dismissWithoutCallback(eq(false /* animate */)); 714 715 // Saved state is restored into new dialog 716 ArgumentCaptor<Bundle> captor2 = ArgumentCaptor.forClass(Bundle.class); 717 verify(mDialog2).show(any(), captor2.capture()); 718 719 // TODO: This should check all values we want to save/restore 720 assertEquals(captor.getValue(), captor2.getValue()); 721 } 722 723 @Test testConfigurationPersists_whenBiometricFallbackToCredential()724 public void testConfigurationPersists_whenBiometricFallbackToCredential() { 725 showDialog(new int[] {1} /* sensorIds */, true /* credentialAllowed */); 726 verify(mDialog1).show(any(), any()); 727 728 // Pretend that the UI is now showing device credential UI. 729 doAnswer(invocation -> { 730 Object[] args = invocation.getArguments(); 731 Bundle savedState = (Bundle) args[0]; 732 savedState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false); 733 savedState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, true); 734 return null; // onSaveState returns void 735 }).when(mDialog1).onSaveState(any()); 736 737 mAuthController.onConfigurationChanged(new Configuration()); 738 739 // Check that the new dialog was initialized to the credential UI. 740 ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class); 741 verify(mDialog2).show(any(), captor.capture()); 742 assertEquals(Authenticators.DEVICE_CREDENTIAL, 743 mAuthController.mLastBiometricPromptInfo.getAuthenticators()); 744 } 745 746 @Test testClientNotified_whenTaskStackChangesDuringShow()747 public void testClientNotified_whenTaskStackChangesDuringShow() throws Exception { 748 switchTask("other_package"); 749 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 750 751 waitForIdleSync(); 752 753 assertNull(mAuthController.mCurrentDialog); 754 assertNull(mAuthController.mReceiver); 755 verify(mDialog1).dismissWithoutCallback(true /* animate */); 756 verify(mReceiver).onDialogDismissed( 757 eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), 758 eq(null) /* credentialAttestation */); 759 } 760 761 @Test testClientNotified_whenTaskStackChangesDuringAuthentication()762 public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception { 763 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 764 765 switchTask("other_package"); 766 767 mAuthController.mTaskStackListener.onTaskStackChanged(); 768 waitForIdleSync(); 769 770 assertNull(mAuthController.mCurrentDialog); 771 assertNull(mAuthController.mReceiver); 772 verify(mDialog1).dismissWithoutCallback(true /* animate */); 773 verify(mReceiver).onDialogDismissed( 774 eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), 775 eq(null) /* credentialAttestation */); 776 } 777 778 @Test testDoesNotCrash_whenTryAgainPressedAfterDismissal()779 public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() { 780 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 781 final long requestID = mAuthController.mCurrentDialog.getRequestId(); 782 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, 783 null, /* credentialAttestation */requestID); 784 mAuthController.onTryAgainPressed(requestID); 785 } 786 787 @Test testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal()788 public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() { 789 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 790 final long requestID = mAuthController.mCurrentDialog.getRequestId(); 791 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, 792 null /* credentialAttestation */, requestID); 793 mAuthController.onDeviceCredentialPressed(requestID); 794 } 795 796 @Test testActionCloseSystemDialogs_dismissesDialogIfShowing()797 public void testActionCloseSystemDialogs_dismissesDialogIfShowing() throws Exception { 798 showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); 799 Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 800 mAuthController.mBroadcastReceiver.onReceive(mContext, intent); 801 waitForIdleSync(); 802 803 assertNull(mAuthController.mCurrentDialog); 804 assertNull(mAuthController.mReceiver); 805 verify(mDialog1).dismissWithoutCallback(true /* animate */); 806 verify(mReceiver).onDialogDismissed( 807 eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), 808 eq(null) /* credentialAttestation */); 809 } 810 811 @Test testOnAodInterrupt()812 public void testOnAodInterrupt() { 813 final int pos = 10; 814 final float majorMinor = 5f; 815 mAuthController.onAodInterrupt(pos, pos, majorMinor, majorMinor); 816 verify(mUdfpsController).onAodInterrupt(eq(pos), eq(pos), eq(majorMinor), eq(majorMinor)); 817 } 818 819 @Test testSubscribesToOrientationChangesOnStart()820 public void testSubscribesToOrientationChangesOnStart() { 821 verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong()); 822 } 823 824 @Test testOnBiometricPromptShownCallback()825 public void testOnBiometricPromptShownCallback() { 826 // GIVEN a callback is registered 827 AuthController.Callback callback = mock(AuthController.Callback.class); 828 mAuthController.addCallback(callback); 829 830 // WHEN dialog is shown 831 showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); 832 833 // THEN callback should be received 834 verify(callback).onBiometricPromptShown(); 835 } 836 837 @Test testOnBiometricPromptDismissedCallback()838 public void testOnBiometricPromptDismissedCallback() { 839 // GIVEN a callback is registered 840 AuthController.Callback callback = mock(AuthController.Callback.class); 841 mAuthController.addCallback(callback); 842 843 // WHEN dialog is shown and then dismissed 844 showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); 845 mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, 846 null /* credentialAttestation */, 847 mAuthController.mCurrentDialog.getRequestId()); 848 849 // THEN callback should be received 850 verify(callback).onBiometricPromptDismissed(); 851 } 852 853 @Test testSubscribesToLogContext()854 public void testSubscribesToLogContext() { 855 mAuthController.setBiometricContextListener(mContextListener); 856 verify(mLogContextInteractor).addBiometricContextListener(same(mContextListener)); 857 } 858 859 @Test testGetFingerprintSensorLocationChanges_differentRotations()860 public void testGetFingerprintSensorLocationChanges_differentRotations() { 861 // GIVEN fp default location and mocked device dimensions 862 // Rotation 0, where "o" is the location of the FP sensor, if x or y = 0, it's the edge of 863 // the screen which is why a 1x1 width and height is represented by a 2x2 grid below: 864 // [* o] 865 // [* *] 866 Point fpDefaultLocation = new Point(1, 0); 867 final DisplayInfo displayInfo = new DisplayInfo(); 868 displayInfo.logicalWidth = 1; 869 displayInfo.logicalHeight = 1; 870 871 // WHEN the rotation is 0, THEN no rotation applied 872 displayInfo.rotation = Surface.ROTATION_0; 873 assertEquals( 874 fpDefaultLocation, 875 mAuthController.rotateToCurrentOrientation( 876 new Point(fpDefaultLocation), displayInfo) 877 ); 878 879 // WHEN the rotation is 270, THEN rotation is applied 880 // [* *] 881 // [* o] 882 displayInfo.rotation = Surface.ROTATION_270; 883 assertEquals( 884 new Point(1, 1), 885 mAuthController.rotateToCurrentOrientation( 886 new Point(fpDefaultLocation), displayInfo) 887 ); 888 889 // WHEN the rotation is 180, THEN rotation is applied 890 // [* *] 891 // [o *] 892 displayInfo.rotation = Surface.ROTATION_180; 893 assertEquals( 894 new Point(0, 1), 895 mAuthController.rotateToCurrentOrientation( 896 new Point(fpDefaultLocation), displayInfo) 897 ); 898 899 // WHEN the rotation is 90, THEN rotation is applied 900 // [o *] 901 // [* *] 902 displayInfo.rotation = Surface.ROTATION_90; 903 assertEquals( 904 new Point(0, 0), 905 mAuthController.rotateToCurrentOrientation( 906 new Point(fpDefaultLocation), displayInfo) 907 ); 908 } 909 910 @Test testGetFingerprintSensorLocationChanges_rotateRectangle()911 public void testGetFingerprintSensorLocationChanges_rotateRectangle() { 912 // GIVEN fp default location and mocked device dimensions 913 // Rotation 0, where "o" is the location of the FP sensor, if x or y = 0, it's the edge of 914 // the screen. 915 // [* * o *] 916 // [* * * *] 917 Point fpDefaultLocation = new Point(2, 0); 918 final DisplayInfo displayInfo = new DisplayInfo(); 919 displayInfo.logicalWidth = 3; 920 displayInfo.logicalHeight = 1; 921 922 // WHEN the rotation is 0, THEN no rotation applied 923 displayInfo.rotation = Surface.ROTATION_0; 924 assertEquals( 925 fpDefaultLocation, 926 mAuthController.rotateToCurrentOrientation( 927 new Point(fpDefaultLocation), displayInfo) 928 ); 929 930 // WHEN the rotation is 180, THEN rotation is applied 931 // [* * * *] 932 // [* o * *] 933 displayInfo.rotation = Surface.ROTATION_180; 934 assertEquals( 935 new Point(1, 1), 936 mAuthController.rotateToCurrentOrientation( 937 new Point(fpDefaultLocation), displayInfo) 938 ); 939 940 // Rotation 270 & 90 have swapped logical width and heights 941 displayInfo.logicalWidth = 1; 942 displayInfo.logicalHeight = 3; 943 944 // WHEN the rotation is 270, THEN rotation is applied 945 // [* *] 946 // [* *] 947 // [* o] 948 // [* *] 949 displayInfo.rotation = Surface.ROTATION_270; 950 assertEquals( 951 new Point(1, 2), 952 mAuthController.rotateToCurrentOrientation( 953 new Point(fpDefaultLocation), displayInfo) 954 ); 955 956 // WHEN the rotation is 90, THEN rotation is applied 957 // [* *] 958 // [o *] 959 // [* *] 960 // [* *] 961 displayInfo.rotation = Surface.ROTATION_90; 962 assertEquals( 963 new Point(0, 1), 964 mAuthController.rotateToCurrentOrientation( 965 new Point(fpDefaultLocation), displayInfo) 966 ); 967 } 968 969 @Test testUpdateFingerprintLocation_defaultPointChanges_whenConfigChanges()970 public void testUpdateFingerprintLocation_defaultPointChanges_whenConfigChanges() { 971 when(mContextSpy.getResources()).thenReturn(mResources); 972 973 doReturn(500).when(mResources) 974 .getDimensionPixelSize(eq(com.android.systemui.R.dimen 975 .physical_fingerprint_sensor_center_screen_location_y)); 976 mAuthController.onConfigurationChanged(null /* newConfig */); 977 978 final Point firstFpLocation = mAuthController.getFingerprintSensorLocation(); 979 980 doReturn(1000).when(mResources) 981 .getDimensionPixelSize(eq(com.android.systemui.R.dimen 982 .physical_fingerprint_sensor_center_screen_location_y)); 983 mAuthController.onConfigurationChanged(null /* newConfig */); 984 985 assertNotSame(firstFpLocation, mAuthController.getFingerprintSensorLocation()); 986 } 987 988 @Test testCloseDialog_whenGlobalActionsMenuShown()989 public void testCloseDialog_whenGlobalActionsMenuShown() throws Exception { 990 showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); 991 mAuthController.handleShowGlobalActionsMenu(); 992 verify(mReceiver).onDialogDismissed( 993 eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), 994 eq(null) /* credentialAttestation */); 995 } 996 997 @Test testShowDialog_whenOwnerNotInForeground()998 public void testShowDialog_whenOwnerNotInForeground() { 999 PromptInfo promptInfo = createTestPromptInfo(); 1000 promptInfo.setAllowBackgroundAuthentication(false); 1001 switchTask("other_package"); 1002 mAuthController.showAuthenticationDialog(promptInfo, 1003 mReceiver /* receiver */, 1004 new int[]{1} /* sensorIds */, 1005 false /* credentialAllowed */, 1006 true /* requireConfirmation */, 1007 0 /* userId */, 1008 0 /* operationId */, 1009 "testPackage", 1010 REQUEST_ID); 1011 1012 assertNull(mAuthController.mCurrentDialog); 1013 verify(mDialog1, never()).show(any(), any()); 1014 } 1015 showDialog(int[] sensorIds, boolean credentialAllowed)1016 private void showDialog(int[] sensorIds, boolean credentialAllowed) { 1017 mAuthController.showAuthenticationDialog(createTestPromptInfo(), 1018 mReceiver /* receiver */, 1019 sensorIds, 1020 credentialAllowed, 1021 true /* requireConfirmation */, 1022 0 /* userId */, 1023 0 /* operationId */, 1024 "testPackage", 1025 REQUEST_ID); 1026 } 1027 switchTask(String packageName)1028 private void switchTask(String packageName) { 1029 final List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>(); 1030 final ActivityManager.RunningTaskInfo taskInfo = 1031 mock(ActivityManager.RunningTaskInfo.class); 1032 taskInfo.topActivity = mock(ComponentName.class); 1033 when(taskInfo.topActivity.getPackageName()).thenReturn(packageName); 1034 tasks.add(taskInfo); 1035 when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); 1036 } 1037 createTestPromptInfo()1038 private PromptInfo createTestPromptInfo() { 1039 PromptInfo promptInfo = new PromptInfo(); 1040 1041 promptInfo.setTitle("Title"); 1042 promptInfo.setSubtitle("Subtitle"); 1043 promptInfo.setDescription("Description"); 1044 promptInfo.setNegativeButtonText("Negative Button"); 1045 1046 // RequireConfirmation is a hint to BiometricService. This can be forced to be required 1047 // by user settings, and should be tested in BiometricService. 1048 promptInfo.setConfirmationRequested(true); 1049 1050 return promptInfo; 1051 } 1052 generateRandomHAT()1053 private byte[] generateRandomHAT() { 1054 byte[] HAT = new byte[69]; 1055 Random random = new Random(); 1056 random.nextBytes(HAT); 1057 return HAT; 1058 } 1059 enrollFingerprintAndFace(final int userId)1060 private void enrollFingerprintAndFace(final int userId) { 1061 1062 // Enroll fingerprint 1063 verify(mFingerprintManager).registerBiometricStateListener( 1064 mBiometricStateCaptor.capture()); 1065 assertFalse(mAuthController.isFingerprintEnrolled(userId)); 1066 1067 mBiometricStateCaptor.getValue().onEnrollmentsChanged(userId, 1068 1 /* sensorId */, true /* hasEnrollments */); 1069 waitForIdleSync(); 1070 1071 assertTrue(mAuthController.isFingerprintEnrolled(userId)); 1072 1073 // Enroll face 1074 verify(mFaceManager).registerBiometricStateListener( 1075 mBiometricStateCaptor.capture()); 1076 assertFalse(mAuthController.isFaceAuthEnrolled(userId)); 1077 1078 mBiometricStateCaptor.getValue().onEnrollmentsChanged(userId, 1079 2 /* sensorId */, true /* hasEnrollments */); 1080 waitForIdleSync(); 1081 1082 assertTrue(mAuthController.isFaceAuthEnrolled(userId)); 1083 } 1084 1085 private final class TestableAuthController extends AuthController { 1086 private int mBuildCount = 0; 1087 private PromptInfo mLastBiometricPromptInfo; 1088 TestableAuthController(Context context)1089 TestableAuthController(Context context) { 1090 super(context, mFeatureFlags, null /* applicationCoroutineScope */, 1091 mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, 1092 mFingerprintManager, mFaceManager, () -> mUdfpsController, 1093 () -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle, 1094 mPanelInteractionDetector, mUserManager, mLockPatternUtils, mUdfpsLogger, 1095 mLogContextInteractor, () -> mBiometricPromptCredentialInteractor, 1096 () -> mPromptSelectionInteractor, () -> mCredentialViewModel, 1097 () -> mPromptViewModel, mInteractionJankMonitor, mHandler, mBackgroundExecutor, 1098 mUdfpsUtils, mVibratorHelper); 1099 } 1100 1101 @Override buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, WakefulnessLifecycle wakefulnessLifecycle, AuthDialogPanelInteractionDetector panelInteractionDetector, UserManager userManager, LockPatternUtils lockPatternUtils, PromptViewModel viewModel)1102 protected AuthDialog buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo, 1103 boolean requireConfirmation, int userId, int[] sensorIds, 1104 String opPackageName, boolean skipIntro, long operationId, long requestId, 1105 WakefulnessLifecycle wakefulnessLifecycle, 1106 AuthDialogPanelInteractionDetector panelInteractionDetector, 1107 UserManager userManager, 1108 LockPatternUtils lockPatternUtils, PromptViewModel viewModel) { 1109 1110 mLastBiometricPromptInfo = promptInfo; 1111 1112 AuthDialog dialog; 1113 if (mBuildCount == 0) { 1114 dialog = mDialog1; 1115 } else if (mBuildCount == 1) { 1116 dialog = mDialog2; 1117 } else { 1118 dialog = null; 1119 } 1120 mBuildCount++; 1121 return dialog; 1122 } 1123 } 1124 1125 @Override waitForIdleSync()1126 protected void waitForIdleSync() { 1127 mTestableLooper.processAllMessages(); 1128 } 1129 } 1130