1 /* 2 * Copyright (C) 2020 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.server.biometrics.sensors; 18 19 import static android.testing.TestableLooper.RunWithLooper; 20 21 import static junit.framework.Assert.assertTrue; 22 import static junit.framework.Assert.fail; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertFalse; 26 import static org.junit.Assert.assertNotEquals; 27 import static org.junit.Assert.assertNull; 28 import static org.mockito.ArgumentMatchers.any; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.ArgumentMatchers.eq; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 import static org.mockito.Mockito.withSettings; 36 37 import android.content.Context; 38 import android.hardware.biometrics.BiometricConstants; 39 import android.hardware.biometrics.IBiometricService; 40 import android.os.Binder; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.RemoteException; 44 import android.platform.test.annotations.Presubmit; 45 import android.testing.AndroidTestingRunner; 46 import android.testing.TestableContext; 47 import android.testing.TestableLooper; 48 49 import androidx.annotation.NonNull; 50 import androidx.annotation.Nullable; 51 import androidx.test.InstrumentationRegistry; 52 import androidx.test.filters.SmallTest; 53 54 import com.android.server.biometrics.nano.BiometricSchedulerProto; 55 import com.android.server.biometrics.nano.BiometricsProto; 56 57 import org.junit.Before; 58 import org.junit.Rule; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 import org.mockito.Mock; 62 import org.mockito.MockitoAnnotations; 63 64 @Presubmit 65 @SmallTest 66 @RunWith(AndroidTestingRunner.class) 67 @RunWithLooper(setAsMainLooper = true) 68 public class BiometricSchedulerTest { 69 70 private static final String TAG = "BiometricSchedulerTest"; 71 private static final int TEST_SENSOR_ID = 1; 72 private static final int LOG_NUM_RECENT_OPERATIONS = 2; 73 74 private BiometricScheduler mScheduler; 75 private IBinder mToken; 76 77 @Mock 78 private IBiometricService mBiometricService; 79 80 @Rule 81 public final TestableContext mContext = 82 new TestableContext(InstrumentationRegistry.getContext(), null); 83 84 @Before setUp()85 public void setUp() { 86 MockitoAnnotations.initMocks(this); 87 mToken = new Binder(); 88 mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()), 89 BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */, 90 mBiometricService, LOG_NUM_RECENT_OPERATIONS, 91 CoexCoordinator.getInstance()); 92 } 93 94 @Test testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash()95 public void testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash() { 96 final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class); 97 98 final HalClientMonitor<Object> client1 = 99 new TestHalClientMonitor(mContext, mToken, nonNullDaemon); 100 final HalClientMonitor<Object> client2 = 101 new TestHalClientMonitor(mContext, mToken, nonNullDaemon); 102 mScheduler.scheduleClientMonitor(client1); 103 mScheduler.scheduleClientMonitor(client2); 104 105 client1.mCallback.onClientFinished(client1, true /* success */); 106 client1.mCallback.onClientFinished(client1, true /* success */); 107 } 108 109 @Test testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt()110 public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() { 111 // Even if second client has a non-null daemon, it needs to be canceled. 112 final TestHalClientMonitor client1 = new TestHalClientMonitor( 113 mContext, mToken, () -> null); 114 final TestHalClientMonitor client2 = new TestHalClientMonitor( 115 mContext, mToken, () -> mock(Object.class)); 116 117 final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class); 118 final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class); 119 120 // Pretend the scheduler is busy so the first operation doesn't start right away. We want 121 // to pretend like there are two operations in the queue before kicking things off 122 mScheduler.mCurrentOperation = new BiometricSchedulerOperation( 123 mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class)); 124 125 mScheduler.scheduleClientMonitor(client1, callback1); 126 assertEquals(1, mScheduler.mPendingOperations.size()); 127 // client1 is pending. Allow the scheduler to start once second client is added. 128 mScheduler.mCurrentOperation = null; 129 mScheduler.scheduleClientMonitor(client2, callback2); 130 waitForIdle(); 131 132 assertTrue(client1.mUnableToStart); 133 verify(callback1).onClientFinished(eq(client1), eq(false) /* success */); 134 verify(callback1, never()).onClientStarted(any()); 135 136 assertTrue(client2.mUnableToStart); 137 verify(callback2).onClientFinished(eq(client2), eq(false) /* success */); 138 verify(callback2, never()).onClientStarted(any()); 139 140 assertTrue(mScheduler.mPendingOperations.isEmpty()); 141 } 142 143 @Test testRemovesOnlyBiometricPromptOperation_whenNullHal()144 public void testRemovesOnlyBiometricPromptOperation_whenNullHal() throws Exception { 145 // Second non-BiometricPrompt client has a valid daemon 146 final Object daemon2 = mock(Object.class); 147 148 final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class); 149 150 final TestAuthenticationClient client1 = 151 new TestAuthenticationClient(mContext, () -> null, mToken, listener1); 152 final TestHalClientMonitor client2 = 153 new TestHalClientMonitor(mContext, mToken, () -> daemon2); 154 155 final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class); 156 final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class); 157 158 // Pretend the scheduler is busy so the first operation doesn't start right away. We want 159 // to pretend like there are two operations in the queue before kicking things off 160 mScheduler.mCurrentOperation = new BiometricSchedulerOperation( 161 mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class)); 162 163 mScheduler.scheduleClientMonitor(client1, callback1); 164 assertEquals(1, mScheduler.mPendingOperations.size()); 165 // client1 is pending. Allow the scheduler to start once second client is added. 166 mScheduler.mCurrentOperation = null; 167 mScheduler.scheduleClientMonitor(client2, callback2); 168 waitForIdle(); 169 170 // Simulate that the BiometricPrompt client's sensor is ready 171 mScheduler.startPreparedClient(client1.getCookie()); 172 173 // Client 1 cleans up properly 174 verify(listener1).onError(eq(TEST_SENSOR_ID), anyInt(), 175 eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(0)); 176 verify(callback1).onClientFinished(eq(client1), eq(false) /* success */); 177 verify(callback1, never()).onClientStarted(any()); 178 179 // Client 2 was able to start 180 assertFalse(client2.mUnableToStart); 181 assertTrue(client2.mStarted); 182 verify(callback2).onClientStarted(eq(client2)); 183 } 184 185 @Test testCancelNotInvoked_whenOperationWaitingForCookie()186 public void testCancelNotInvoked_whenOperationWaitingForCookie() { 187 final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class); 188 final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, 189 lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class)); 190 final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class); 191 192 // Schedule a BiometricPrompt authentication request 193 mScheduler.scheduleClientMonitor(client1, callback1); 194 195 assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); 196 assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor()); 197 assertEquals(0, mScheduler.mPendingOperations.size()); 198 199 // Request it to be canceled. The operation can be canceled immediately, and the scheduler 200 // should go back to idle, since in this case the framework has not even requested the HAL 201 // to authenticate yet. 202 mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */); 203 waitForIdle(); 204 assertTrue(client1.isAlreadyDone()); 205 assertTrue(client1.mDestroyed); 206 assertFalse(client1.mStartedHal); 207 assertNull(mScheduler.mCurrentOperation); 208 } 209 210 @Test testProtoDump_singleCurrentOperation()211 public void testProtoDump_singleCurrentOperation() throws Exception { 212 // Nothing so far 213 BiometricSchedulerProto bsp = getDump(true /* clearSchedulerBuffer */); 214 assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation); 215 assertEquals(0, bsp.totalOperations); 216 // TODO:(b/178828362) See bug and/or commit message :/ 217 // assertEquals(0, bsp.recentOperations.length); 218 219 // Pretend the scheduler is busy enrolling, and check the proto dump again. 220 final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken, 221 () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL); 222 mScheduler.scheduleClientMonitor(client); 223 waitForIdle(); 224 bsp = getDump(true /* clearSchedulerBuffer */); 225 assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation); 226 // No operations have completed yet 227 assertEquals(0, bsp.totalOperations); 228 229 // TODO:(b/178828362) See bug and/or commit message :/ 230 assertEquals(1, bsp.recentOperations.length); 231 assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]); 232 233 // Finish this operation, so the next scheduled one can start 234 client.getCallback().onClientFinished(client, true); 235 } 236 237 @Test testProtoDump_fifo()238 public void testProtoDump_fifo() throws Exception { 239 // Add the first operation 240 final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken, 241 () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL); 242 mScheduler.scheduleClientMonitor(client); 243 waitForIdle(); 244 BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */); 245 assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation); 246 // No operations have completed yet 247 assertEquals(0, bsp.totalOperations); 248 // TODO:(b/178828362) See bug and/or commit message :/ 249 // assertEquals(0, bsp.recentOperations.length); 250 // Finish this operation, so the next scheduled one can start 251 client.getCallback().onClientFinished(client, true); 252 253 // Add another operation 254 final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken, 255 () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE); 256 mScheduler.scheduleClientMonitor(client2); 257 waitForIdle(); 258 bsp = getDump(false /* clearSchedulerBuffer */); 259 assertEquals(BiometricsProto.CM_REMOVE, bsp.currentOperation); 260 assertEquals(1, bsp.totalOperations); // Enroll finished 261 assertEquals(1, bsp.recentOperations.length); 262 assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]); 263 client2.getCallback().onClientFinished(client2, true); 264 265 // And another operation 266 final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken, 267 () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE); 268 mScheduler.scheduleClientMonitor(client3); 269 waitForIdle(); 270 bsp = getDump(false /* clearSchedulerBuffer */); 271 assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.currentOperation); 272 assertEquals(2, bsp.totalOperations); 273 assertEquals(2, bsp.recentOperations.length); 274 assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]); 275 assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[1]); 276 277 // Finish the last operation, and check that the first operation is removed from the FIFO. 278 // The test initializes the scheduler with "LOG_NUM_RECENT_OPERATIONS = 2" :) 279 client3.getCallback().onClientFinished(client3, true); 280 waitForIdle(); 281 bsp = getDump(true /* clearSchedulerBuffer */); 282 assertEquals(3, bsp.totalOperations); 283 assertEquals(2, bsp.recentOperations.length); 284 assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[0]); 285 assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.recentOperations[1]); 286 // Nothing is currently running anymore 287 assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation); 288 289 // RecentOperations queue is cleared (by the previous dump) 290 bsp = getDump(true /* clearSchedulerBuffer */); 291 292 // TODO:(b/178828362) See bug and/or commit message :/ 293 assertEquals(1, bsp.recentOperations.length); 294 assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]); 295 } 296 297 @Test testCancelPendingAuth()298 public void testCancelPendingAuth() throws RemoteException { 299 final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); 300 final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon); 301 final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); 302 final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon, 303 mToken, callback); 304 305 // Add a non-cancellable client, then add the auth client 306 mScheduler.scheduleClientMonitor(client1); 307 mScheduler.scheduleClientMonitor(client2); 308 waitForIdle(); 309 310 assertEquals(mScheduler.getCurrentClient(), client1); 311 assertFalse(mScheduler.mPendingOperations.getFirst().isStarted()); 312 313 // Request cancel before the authentication client has started 314 mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */); 315 waitForIdle(); 316 assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling()); 317 318 // Finish the blocking client. The authentication client should send ERROR_CANCELED 319 client1.getCallback().onClientFinished(client1, true /* success */); 320 waitForIdle(); 321 verify(callback).onError(anyInt(), anyInt(), 322 eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), 323 eq(0) /* vendorCode */); 324 assertNull(mScheduler.getCurrentClient()); 325 assertTrue(client1.isAlreadyDone()); 326 assertTrue(client1.mDestroyed); 327 assertTrue(client2.isAlreadyDone()); 328 assertTrue(client2.mDestroyed); 329 } 330 331 @Test testCancels_whenAuthRequestIdNotSet()332 public void testCancels_whenAuthRequestIdNotSet() { 333 testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */); 334 } 335 336 @Test testCancels_whenAuthRequestIdNotSet_notStarted()337 public void testCancels_whenAuthRequestIdNotSet_notStarted() { 338 testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */); 339 } 340 341 @Test testCancels_whenAuthRequestIdMatches()342 public void testCancels_whenAuthRequestIdMatches() { 343 testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */); 344 } 345 346 @Test testCancels_whenAuthRequestIdMatches_noStarted()347 public void testCancels_whenAuthRequestIdMatches_noStarted() { 348 testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */); 349 } 350 351 @Test testDoesNotCancel_whenAuthRequestIdMismatched()352 public void testDoesNotCancel_whenAuthRequestIdMismatched() { 353 testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */); 354 } 355 356 @Test testDoesNotCancel_whenAuthRequestIdMismatched_notStarted()357 public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() { 358 testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */); 359 } 360 testCancelsAuthDetectWhenRequestId(@ullable Long requestId, long cancelRequestId, boolean started)361 private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId, 362 boolean started) { 363 final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); 364 final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); 365 testCancelsWhenRequestId(requestId, cancelRequestId, started, 366 new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback)); 367 } 368 369 @Test testCancels_whenEnrollRequestIdNotSet()370 public void testCancels_whenEnrollRequestIdNotSet() { 371 testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */); 372 } 373 374 @Test testCancels_whenEnrollRequestIdMatches()375 public void testCancels_whenEnrollRequestIdMatches() { 376 testCancelsEnrollWhenRequestId(200L, 200, false /* started */); 377 } 378 379 @Test testDoesNotCancel_whenEnrollRequestIdMismatched()380 public void testDoesNotCancel_whenEnrollRequestIdMismatched() { 381 testCancelsEnrollWhenRequestId(10L, 20, false /* started */); 382 } 383 testCancelsEnrollWhenRequestId(@ullable Long requestId, long cancelRequestId, boolean started)384 private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId, 385 boolean started) { 386 final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); 387 final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); 388 testCancelsWhenRequestId(requestId, cancelRequestId, started, 389 new TestEnrollClient(mContext, lazyDaemon, mToken, callback)); 390 } 391 testCancelsWhenRequestId(@ullable Long requestId, long cancelRequestId, boolean started, HalClientMonitor<?> client)392 private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId, 393 boolean started, HalClientMonitor<?> client) { 394 final boolean matches = requestId == null || requestId == cancelRequestId; 395 if (requestId != null) { 396 client.setRequestId(requestId); 397 } 398 399 final boolean isAuth = client instanceof TestAuthenticationClient; 400 final boolean isEnroll = client instanceof TestEnrollClient; 401 402 mScheduler.scheduleClientMonitor(client); 403 if (started) { 404 mScheduler.startPreparedClient(client.getCookie()); 405 } 406 waitForIdle(); 407 if (isAuth) { 408 mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId); 409 } else if (isEnroll) { 410 mScheduler.cancelEnrollment(mToken, cancelRequestId); 411 } else { 412 fail("unexpected operation type"); 413 } 414 waitForIdle(); 415 416 if (isAuth) { 417 // auth clients that were waiting for cookie when canceled should never invoke the hal 418 final TestAuthenticationClient authClient = (TestAuthenticationClient) client; 419 assertEquals(matches && started ? 1 : 0, authClient.mNumCancels); 420 assertEquals(started, authClient.mStartedHal); 421 } else if (isEnroll) { 422 final TestEnrollClient enrollClient = (TestEnrollClient) client; 423 assertEquals(matches ? 1 : 0, enrollClient.mNumCancels); 424 assertTrue(enrollClient.mStartedHal); 425 } 426 427 if (matches) { 428 if (started || isEnroll) { // prep'd auth clients and enroll clients 429 assertTrue(mScheduler.mCurrentOperation.isCanceling()); 430 } 431 } else { 432 if (started || isEnroll) { // prep'd auth clients and enroll clients 433 assertTrue(mScheduler.mCurrentOperation.isStarted()); 434 } else { 435 assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); 436 } 437 } 438 } 439 440 @Test testCancelsPending_whenAuthRequestIdsSet()441 public void testCancelsPending_whenAuthRequestIdsSet() { 442 final long requestId1 = 10; 443 final long requestId2 = 20; 444 final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); 445 final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); 446 final TestAuthenticationClient client1 = new TestAuthenticationClient( 447 mContext, lazyDaemon, mToken, callback); 448 client1.setRequestId(requestId1); 449 final TestAuthenticationClient client2 = new TestAuthenticationClient( 450 mContext, lazyDaemon, mToken, callback); 451 client2.setRequestId(requestId2); 452 453 mScheduler.scheduleClientMonitor(client1); 454 mScheduler.scheduleClientMonitor(client2); 455 mScheduler.startPreparedClient(client1.getCookie()); 456 waitForIdle(); 457 mScheduler.cancelAuthenticationOrDetection(mToken, 9999); 458 waitForIdle(); 459 460 assertTrue(mScheduler.mCurrentOperation.isStarted()); 461 assertFalse(mScheduler.mPendingOperations.getFirst().isStarted()); 462 463 mScheduler.cancelAuthenticationOrDetection(mToken, requestId2); 464 waitForIdle(); 465 466 assertTrue(mScheduler.mCurrentOperation.isStarted()); 467 assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling()); 468 } 469 470 @Test testInterruptPrecedingClients_whenExpected()471 public void testInterruptPrecedingClients_whenExpected() { 472 final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class, 473 withSettings().extraInterfaces(Interruptable.class)); 474 475 final BaseClientMonitor interrupter = mock(BaseClientMonitor.class); 476 when(interrupter.interruptsPrecedingClients()).thenReturn(true); 477 478 mScheduler.scheduleClientMonitor(interruptableMonitor); 479 mScheduler.scheduleClientMonitor(interrupter); 480 waitForIdle(); 481 482 verify((Interruptable) interruptableMonitor).cancel(); 483 mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */); 484 } 485 486 @Test testDoesNotInterruptPrecedingClients_whenNotExpected()487 public void testDoesNotInterruptPrecedingClients_whenNotExpected() { 488 final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class, 489 withSettings().extraInterfaces(Interruptable.class)); 490 491 final BaseClientMonitor interrupter = mock(BaseClientMonitor.class); 492 when(interrupter.interruptsPrecedingClients()).thenReturn(false); 493 494 mScheduler.scheduleClientMonitor(interruptableMonitor); 495 mScheduler.scheduleClientMonitor(interrupter); 496 waitForIdle(); 497 498 verify((Interruptable) interruptableMonitor, never()).cancel(); 499 } 500 501 @Test testClientDestroyed_afterFinish()502 public void testClientDestroyed_afterFinish() { 503 final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class); 504 final TestHalClientMonitor client = 505 new TestHalClientMonitor(mContext, mToken, nonNullDaemon); 506 mScheduler.scheduleClientMonitor(client); 507 client.mCallback.onClientFinished(client, true /* success */); 508 waitForIdle(); 509 assertTrue(client.mDestroyed); 510 } 511 getDump(boolean clearSchedulerBuffer)512 private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { 513 return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); 514 } 515 516 private static class TestAuthenticationClient extends AuthenticationClient<Object> { 517 boolean mStartedHal = false; 518 boolean mStoppedHal = false; 519 boolean mDestroyed = false; 520 int mNumCancels = 0; 521 TestAuthenticationClient(@onNull Context context, @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener)522 public TestAuthenticationClient(@NonNull Context context, 523 @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token, 524 @NonNull ClientMonitorCallbackConverter listener) { 525 super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */, 526 false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */, 527 TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */, 528 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class), 529 false /* isKeyguard */, true /* shouldVibrate */, 530 false /* isKeyguardBypassEnabled */); 531 } 532 533 @Override stopHalOperation()534 protected void stopHalOperation() { 535 mStoppedHal = true; 536 } 537 538 @Override startHalOperation()539 protected void startHalOperation() { 540 mStartedHal = true; 541 } 542 543 @Override handleLifecycleAfterAuth(boolean authenticated)544 protected void handleLifecycleAfterAuth(boolean authenticated) {} 545 546 @Override wasUserDetected()547 public boolean wasUserDetected() { 548 return false; 549 } 550 551 @Override destroy()552 public void destroy() { 553 mDestroyed = true; 554 super.destroy(); 555 } 556 557 @Override cancel()558 public void cancel() { 559 mNumCancels++; 560 super.cancel(); 561 } 562 } 563 564 private static class TestEnrollClient extends EnrollClient<Object> { 565 boolean mStartedHal = false; 566 boolean mStoppedHal = false; 567 int mNumCancels = 0; 568 TestEnrollClient(@onNull Context context, @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener)569 TestEnrollClient(@NonNull Context context, 570 @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token, 571 @NonNull ClientMonitorCallbackConverter listener) { 572 super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69], 573 "test" /* owner */, mock(BiometricUtils.class), 574 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID, 575 true /* shouldVibrate */); 576 } 577 578 @Override stopHalOperation()579 protected void stopHalOperation() { 580 mStoppedHal = true; 581 } 582 583 @Override startHalOperation()584 protected void startHalOperation() { 585 mStartedHal = true; 586 } 587 588 @Override hasReachedEnrollmentLimit()589 protected boolean hasReachedEnrollmentLimit() { 590 return false; 591 } 592 593 @Override cancel()594 public void cancel() { 595 mNumCancels++; 596 super.cancel(); 597 } 598 } 599 600 private static class TestHalClientMonitor extends HalClientMonitor<Object> { 601 private final int mProtoEnum; 602 private boolean mUnableToStart; 603 private boolean mStarted; 604 private boolean mDestroyed; 605 TestHalClientMonitor(@onNull Context context, @NonNull IBinder token, @NonNull LazyDaemon<Object> lazyDaemon)606 TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token, 607 @NonNull LazyDaemon<Object> lazyDaemon) { 608 this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER); 609 } 610 TestHalClientMonitor(@onNull Context context, @NonNull IBinder token, @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum)611 TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token, 612 @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum) { 613 super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, 614 TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */, 615 0 /* statsAction */, 0 /* statsClient */); 616 mProtoEnum = protoEnum; 617 } 618 619 @Override unableToStart()620 public void unableToStart() { 621 assertFalse(mUnableToStart); 622 mUnableToStart = true; 623 } 624 625 @Override getProtoEnum()626 public int getProtoEnum() { 627 return mProtoEnum; 628 } 629 630 @Override start(@onNull Callback callback)631 public void start(@NonNull Callback callback) { 632 super.start(callback); 633 assertFalse(mStarted); 634 mStarted = true; 635 } 636 637 @Override startHalOperation()638 protected void startHalOperation() { 639 mStarted = true; 640 } 641 642 @Override destroy()643 public void destroy() { 644 super.destroy(); 645 mDestroyed = true; 646 } 647 } 648 waitForIdle()649 private void waitForIdle() { 650 TestableLooper.get(this).processAllMessages(); 651 } 652 } 653